Functions Details

Autodiff.forward

forward API documentation Top

forward module

This file contains the central data structure and functions related to the forward mode auto differentiation.

"""
This file contains the central data structure and functions related to the
forward mode auto differentiation. 
"""

import numpy as np

class Expression:
    """ 
    This is a class for representing expression.
    It is the super class for variable and constant.
    """
    def __init__(self, ele_func, sub_expr1, sub_expr2=None):
        """ 
        The constructor for VectorFunction class. 
        
        PARAMETERS:
        =======
        ele_func: the function creating this expression
        sub_expr1: variable/constant composing this expression
        sub_expr2: variable/constant composing this expression, set to non
        for unary operations
        """
        self._ele_func  = ele_func
        self._sub_expr1 = sub_expr1
        self._sub_expr2 = sub_expr2
        self.val = None
        self.bder=0
    
    def evaluation_at(self, val_dict):
        """ 
        The wrapper function for individual evaluation_at function of 
        self_ele_func
        
        PARAMETERS:
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        a scalar value 
        """
        # self._sub_expr2 is None implies that self._ele_func is an unary operator
        if self._sub_expr2 is None: 
            return self._ele_func.evaluation_at(
                self._sub_expr1, val_dict)
        
        # self._sub_expr2 not None implies that self._ele_func is a binary operator
        else:
            return self._ele_func.evaluation_at(
                self._sub_expr1, self._sub_expr2, val_dict)
    
    def derivative_at(self, var, val_dict, order=1):
        """ 
        The wrapper function for individual derivative_at function of 
        self_ele_func
        
        PARAMETERS:
        =======
        val_dict: a dictionary containing variable name and values.
        var: variable of interests for derivative calculation
        
        RETURNS
        ======== 
        a scalar value 
        """
        
        if type(var) is tuple: order=len(var)
        if var is self: 
            if   order == 1: return 1.0
            else: return 0.0
        
        # sub_expr2 being None implies that _ele_func is an unary operator
        if self._sub_expr2 is None:
            return self._ele_func.derivative_at(
                self._sub_expr1, var, val_dict, order)
        
        # sub_expr2 not None implies that _ele_func is a binary operator
        else:
            return self._ele_func.derivative_at(
                self._sub_expr1, self._sub_expr2, var, val_dict, order)
    
    def back_derivative(self,var,val_dict):
        """
        The wrapper function for individual backderivative_at 
        function of self_ele_func
        
        PARAMETERS:
        =======
        val_dict: a dictionary containing variable name and values. Variables
        in val_dict are of atomic feature and cannot be further decomposed.
        var: variable with respect to which the function calculates derivative   
        
        RETURNS
        ========
        derivative of var with respect to the immediate parent that contain var
        """
        if var is self: return 1.0
        if self._sub_expr2 is None:
            return self._ele_func.backderivative_at(self._sub_expr1,var)
        else:
            return self._ele_func.backderivative_at(self._sub_expr1,
                                                    self._sub_expr2,var)    



    def gradient_at(self, val_dict, returns_dict=False):
        """
        calculate 1st derivative of variables in val_dict using forward mode
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        returns_dict: the format of output
         
        RETURNS
        ========
        derivative of variables in val_dict with respect to the current 
        expression, stored in a dictionary or a 2-D numpy array
        """
        if returns_dict:
            return {v: self.derivative_at(v, val_dict) for v in val_dict.keys()}
        return np.array([self.derivative_at(var, val_dict, order=1) 
                         for var in val_dict.keys()])
    
    def hessian_at(self, val_dict):
        """
        calculate 2nd derivative of variables in val_dict using forward mode
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
         
        RETURNS
        ========
        2nd derivative of variables in val_dict with respect to the current 
        expression, stored in a 2-D list
        """
        return np.array( [ \
                          [self.derivative_at((var1, var2), val_dict, order=2)
                           for var1 in val_dict.keys()]
                          for var2 in val_dict.keys() \
                          ] )
    
    def __neg__(self):
        """ Implement dunder method for neg """
        return Expression(Neg, self)

                
    def __add__(self, another):
        """ Implement dunder method for add """
        if isinstance(another, Expression):
            return Expression(Add, self, another)
        # if the other operand is not an Expression, then it must be a number
        # the number then should be converted to a Constant
        else:
            return Expression(Add, self, Constant(another))
    
    
    def __radd__(self, another):
        """ Implement dunder method for right add """
        if isinstance(another, Expression):
            return Expression(Add, another, self)
        else:
            return Expression(Add, Constant(another), self)
    
    def __sub__(self, another):
        """ Implement dunder method for subtraction """
        if isinstance(another, Expression):
            return Expression(Sub, self, another)
        else:
            return Expression(Sub, self, Constant(another))
    
    def __rsub__(self, another):
        """ Implement dunder method for right subtraction """
        if isinstance(another, Expression):
            return Expression(Sub, another, self)
        else:
            return Expression(Sub, Constant(another), self)
        

    def __mul__(self, another):
        """ Implement dunder method for multiplication """
        if isinstance(another, Expression):
            return Expression(Mul,self,another)
        else:
            return Expression(Mul, self, Constant(another))

    def __rmul__(self, another):
        """ Implement dunder method for right multiplication """
        if isinstance(another, Expression):
            return Expression(Mul,another,self)
        else:
            return Expression(Mul, Constant(another),self)
    
    def __truediv__(self, another):
        """ Implement dunder method for division """
        if isinstance(another, Expression):
            return Expression(Div,self,another)
        else:
            return Expression(Div, self, Constant(another))

    def __rtruediv__(self, another):
        """ Implement dunder method for right division """
        if isinstance(another, Expression):
            return Expression(Div,another,self)
        else:
            return Expression(Div, Constant(another),self)
    
    def __pow__(self,another):
        """ Implement dunder method for power """
        if isinstance(another, Expression):
            return Expression(Pow,self,another)
        else:
            return Expression(Pow, self, Constant(another))
    
    def __rpow__(self,another):
        """ Implement dunder method for right power """
        if isinstance(another, Expression):
            return Expression(Pow,another,self)
        else:
            return Expression(Pow, Constant(another),self)
    
    def __eq__(self, another):
        """ Implement dunder method for equal """
        if not isinstance(another, Expression):
            return False
        return self._ele_func == another._ele_func \
               and self._sub_expr1 == another._sub_expr1 \
               and self._sub_expr2 == another._sub_expr2
               
    def __ne__(self, another):
        """ Implement dunder method not equal """
        return ~self.__eq__(another)
    
    def __hash__(self):
        """ Implement dunder method hash """
        return object.__hash__(self)   

class Variable(Expression):
    """ 
    This is a class for representing variable. 
    """
    def __init__(self):
        """ 
        The constructor for VectorFunction class. 
        It has no parameters: 
        """
        self.val = None
        self.bder = 0
        return
    
    def evaluation_at(self, val_dict):
        """ 
        The function to evaluation the value of variable class
        
        PARAMETERS:
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ======== 
        a scalar value 
        """
        return val_dict[self]
    
    def derivative_at(self, var, val_dict, order=1):
        """ 
        The function calculates derivative of variable class. 
  
        PARAMETERS:
        =======
        val_dict: a dictionary containing variable name and values.
        var: variable whose derivative is the result of this function
        order: default set to 1 for 1st derivative, change to 2 for 
        higher order
        
        RETURNS
        ========
        scalar value  
        """
        if order == 1:
            return 1.0 if var is self else 0.0
        else:
            return 0.0
    
    def __eq__(self, another):
        """ Implement dunder method for equal """
        return another is self
    
    def __ne__(self, another):
        """ Implement dunder method for not equal """
        return ~self.__eq__(another)
    
    def __hash__(self):
        """ Implement dunder method for hash """
        return Expression.__hash__(self) 

class Constant(Expression):
    """ 
    This is a class for representing constant. 
      
    Attributes: 
       val: value of the constant
    """
    def __init__(self, val):
        """ 
        The constructor for VectorFunction class. 
        
        PARAMETERS:
        =======
        val: the value of the constant object
        """
        self.val = val
 
    def evaluation_at(self, val_dict):
        """ 
        The function to evaluation the value of constant class
        
        PARAMETERS:
        ======= 
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        a scalar value 
        """
        return self.val
    
    def derivative_at(self, var, val_dict, order=1):
        """ 
        The function calculates derivative of constant class. 
  
        PARAMETERS:
        ======= 
        val_dict: a dictionary containing variable name and values.
        var: variable whose derivative is the result of this function
        order: default set to 1 for 1st derivative, change to 2 for 
        higher order
        
        RETURNS
        ========
        scalar value  
        """
        return 0.0
    
    def __eq__(self, another):
         """ Implement dunder method for equal """
         if isinstance(another, Constant): return True
         else:                             return False
    
    def __ne__(self, another):
         """ Implement dunder method for not equal """
         return ~self.__eq__(another)
    
    def __hash__(self):
         """ Implement dunder method for hash"""
         return Expression.__hash__(self) 


class VectorFunction:
    """ 
    This is a class for applying operations to a vector of variables. 
      
    Attributes: 
        _exprlist: a list of expressions with respect to which the operations
    are applied 
    """
    def __init__(self, exprlist):
        """ 
        The constructor for VectorFunction class. 
        
        PARAMETERS:
        ======= 
        exprlist: a list of expressions with respect to which the class 
        functions are applied to  
        """
        self._exprlist = exprlist.copy()
    
    def evaluation_at(self, val_dict):
        """ 
        The function to apply evaluation_at to a vector of expressions. 
  
        PARAMETERS:
        ======= 
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        a numpy array containing value of expressions in the self._exprlist. 
        """
        return np.array([expr.evaluation_at(val_dict) 
                        for expr in self._exprlist])
    
    def gradient_at(self, var, val_dict):
        """ 
        The function to apply derivative_at to a vector of expressions. 
  
        PARAMETERS:
        =======
        val_dict: a dictionary containing variable name and values.
        var: variable whose derivative is the result of this function
       
        RETURNS
        ========
        a numpy array containing first derivative of expressions in 
        self._exprlist with respect to var. 
        """
        return np.array([f.derivative_at(var, val_dict) for f in self._exprlist])
    
    def jacobian_at(self, val_dict):
        """ 
        The function to calculate jacobian with respect to atomic variables in 
        val_dict. 
  
        PARAMETERS:
        ======= 
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        a 2-D numpy array containing derivatives of variables in val_dict 
        with resepct to expressions in self._exprlist. 
        """
        return np.array([self.gradient_at(var, val_dict)
                         for var in val_dict.keys()]).transpose()


class Add:
    """ 
    This is a class to wrap up static method related to add operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, sub_expr2, val_dict):
        """
        Compute addition of sub_expr1 with sub_expr2 using inputs of variable
        values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sub_expr1 + sub_expr2
        """
        return sub_expr1.evaluation_at(val_dict) + \
               sub_expr2.evaluation_at(val_dict)
    @staticmethod
    def derivative_at(sub_expr1, sub_expr2, var, val_dict, order=1):
        return sub_expr1.derivative_at(var, val_dict, order) + \
               sub_expr2.derivative_at(var, val_dict, order)
    @staticmethod
    def backderivative_at(sub_expr1,sub_expr2,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant 
        sub_expr2: expression or constant 
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return 1

class Sub:
    """ 
    This is a class to wrap up static method related to sub operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, sub_expr2, val_dict):
        """
        Compute subtraction of sub_expr2 from sub_expr1 using inputs of variable
        values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sub_expr1 - sub_expr2
        """
        return sub_expr1.evaluation_at(val_dict) - \
               sub_expr2.evaluation_at(val_dict)
    @staticmethod
    def derivative_at(sub_expr1, sub_expr2, var, val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return sub_expr1.derivative_at(var, val_dict, order) - \
               sub_expr2.derivative_at(var, val_dict, order)
    @staticmethod
    def backderivative_at(sub_expr1,sub_expr2,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant 
        sub_expr2: expression or constant 
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if var == sub_expr1:
            return 1
        if var == sub_expr2:
            return -1 

class Mul:
    """ 
    This is a class to wrap up static method related to mul operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, sub_expr2, val_dict):
        """
        Compute multiplication of sub_expr1 with sub_expr2 using inputs 
        of variable values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sub_expr1 * sub_expr2
        """
        return sub_expr1.evaluation_at(val_dict) *\
               sub_expr2.evaluation_at(val_dict)
    @staticmethod
    def derivative_at(sub_expr1, sub_expr2, var, val_dict,order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if   order == 1:
            return sub_expr1.derivative_at(var, val_dict) * \
                   sub_expr2.evaluation_at(val_dict)+ \
                   sub_expr1.evaluation_at(val_dict) *\
                   sub_expr2.derivative_at(var, val_dict)
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                term1 = sub_expr1.derivative_at(var, val_dict, order=2) \
                        * sub_expr2.evaluation_at(val_dict)
                term2 = sub_expr2.derivative_at(var, val_dict, order=2) \
                        * sub_expr1.evaluation_at(val_dict)
                term3 = sub_expr1.derivative_at(var1, val_dict, order=1) \
                        * sub_expr2.derivative_at(var2, val_dict, order=1)
                term4 = sub_expr2.derivative_at(var1, val_dict, order=1) \
                        * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2 + term3 + term4
            else:
                return Mul.derivative_at(sub_expr1, sub_expr2, (var, var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,sub_expr2,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant 
        sub_expr2: expression or constant 
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if var == sub_expr1:
            return sub_expr2.val
        else:
            return sub_expr1.val
               
class Div:
    """ 
    This is a class to wrap up static method related to div operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, sub_expr2, val_dict):
        """
        Compute division of sub_expr1 by sub_expr2 using inputs of variable
        values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sub_expr1 / sub_expr2
        """
        return sub_expr1.evaluation_at(val_dict) /\
               sub_expr2.evaluation_at(val_dict)
    @staticmethod
    def derivative_at(sub_expr1, sub_expr2, var, val_dict,order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if   order == 1:
            return  sub_expr1.derivative_at(var, val_dict) / \
                    sub_expr2.evaluation_at(val_dict)- \
                    sub_expr1.evaluation_at(val_dict) *\
                    sub_expr2.derivative_at(var, val_dict)/\
                    sub_expr2.evaluation_at(val_dict)**2
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                f = sub_expr1.evaluation_at(val_dict)
                g = sub_expr2.evaluation_at(val_dict)
                term1 =  1/g    * sub_expr2.derivative_at(var, val_dict, order=2)
                term2 = -f/g**2 * sub_expr1.derivative_at(var, val_dict, order=2)
                term3 = -1/g**2 * sub_expr1.derivative_at(var1, val_dict, order=1) \
                                * sub_expr2.derivative_at(var2, val_dict, order=1)
                term4 = -1/g**2 * sub_expr1.derivative_at(var2, val_dict, order=1) \
                                * sub_expr2.derivative_at(var1, val_dict, order=1)
                term5 = 2*f/g**3 * sub_expr2.derivative_at(var1, val_dict, order=1) \
                                 * sub_expr2.derivative_at(var2, val_dict, order=1)
                return term1 + term2 + term3 + term4 + term5  
            else:
                return Div.derivative_at(sub_expr1, sub_expr2, (var, var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,sub_expr2,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant 
        sub_expr2: expression or constant 
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if var == sub_expr1:
            return 1/sub_expr2.val
        elif var == sub_expr2:
            return -sub_expr1.val/sub_expr2.val**2
            
class Pow:
    """ 
    This is a class to wrap up static method related to pow operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, sub_expr2, val_dict):
        """
        Compute sub_expr1 to the sub_expr2 power using inputs of variable
        values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sub_expr1 ** sub_expr2
        """
        return np.power(sub_expr1.evaluation_at(val_dict), 
                        sub_expr2.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1, sub_expr2, var, val_dict,order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        p = sub_expr2.evaluation_at(val_dict)
        if   order == 1:
            return p*np.power(sub_expr1.evaluation_at(val_dict), p-1.0) \
                   * sub_expr1.derivative_at(var, val_dict)
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                term1 = p*np.power(sub_expr1.evaluation_at(val_dict), p-1.0) \
                        * sub_expr1.derivative_at((var1, var2), val_dict, order=2)
                term2 = p*(p-1.0)*np.power(sub_expr1.evaluation_at(val_dict), p-2.0) \
                        * sub_expr1.derivative_at(var1, val_dict, order=1) \
                        * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2
            else:
                return Pow.derivative_at(sub_expr1, sub_expr2, (var, var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,sub_expr2,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        p = sub_expr2.val
        return p*np.power(sub_expr1.val, p-1.0)

def power(expr, p):
    return Expression(Pow, expr, Constant(p))
def sqrt(expr):
    return Expression(Pow, expr, Constant(0.5))


class Exp:
    """ 
    This is a class to wrap up static method related to exp operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, val_dict):
        """
        Compute exponent of sub_expr1 using inputs of variable
        values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        exponent(sub_expr1)
        """
        return np.exp(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1, var, val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if   order == 1:
            return sub_expr1.derivative_at(var, val_dict) * \
                   np.exp(sub_expr1.evaluation_at(val_dict))
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                f = sub_expr1.evaluation_at(val_dict)
                term1 = np.exp(f) * sub_expr1.derivative_at(var,  val_dict, order=2)
                term2 = np.exp(f) * sub_expr1.derivative_at(var1, val_dict, order=1) \
                                  * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2
            else:
                return Exp.derivative_at(sub_expr1, (var,var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return np.exp(sub_expr1.val)
    
def exp(expr):
    return Expression(Exp, expr)

class Log:
    
    @staticmethod
    def evaluation_at(sub_expr1, val_dict):
        return np.log(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1, var, val_dict, order=1):
        if   order == 1:
            return 1 / sub_expr1.evaluation_at(val_dict) * sub_expr1.derivative_at(var, val_dict)
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                f = sub_expr1.evaluation_at(val_dict)
                term1 = 1/f * sub_expr1.derivative_at(var,  val_dict, order=2)
                term2 = -1/f**2 * sub_expr1.derivative_at(var1, val_dict, order=1) \
                            * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2
            else:
                return Log.derivative_at(sub_expr1, (var,var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    def backderivative_at(sub_expr1,var):
        if sub_expr1 == var:
            return 1/sub_expr1.val

def log(expr):
    return Expression(Log, expr)
        
class Neg:
    """ 
    This is a class to wrap up static method related to neg operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, val_dict):
        """
        Compute negation of sub_expr1 using inputs of variable
        values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        negate sub_expr1
        """
        return -sub_expr1.evaluation_at(val_dict)
    
    @staticmethod
    def derivative_at(sub_expr1, var, val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return -sub_expr1.derivative_at(var, val_dict, order)
    
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant 
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return -1


class Sin:
    """ 
    This is a class to wrap up static method related to sin operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, val_dict):
        """
        Compute sin of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sin of sub_expr1 
        """
        return np.sin(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1, var, val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant 
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if   order == 1:
            return sub_expr1.derivative_at(var, val_dict) * \
        np.cos(sub_expr1.evaluation_at(val_dict))
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                f = sub_expr1.evaluation_at(val_dict)
                term1 =  np.cos(f) * sub_expr1.derivative_at(var,  val_dict, order=2)
                term2 = -np.sin(f) * sub_expr1.derivative_at(var1, val_dict, order=1) \
                                   * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2
            else:
                return Sin.derivative_at(sub_expr1, (var,var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return np.cos(sub_expr1.val)
        
def sin(expr):
    return Expression(Sin, expr)

class Cos:
    """ 
    This is a class to wrap up static method related to cos operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute cos of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        cos sub_expr1
        """
        return np.cos(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if   order == 1:
            return -sub_expr1.derivative_at(var, val_dict, order) * \
                   np.sin(sub_expr1.evaluation_at(val_dict)) 
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                f = sub_expr1.evaluation_at(val_dict)
                term1 = -np.sin(f) * sub_expr1.derivative_at(var,  val_dict, order=2)
                term2 = -np.cos(f) * sub_expr1.derivative_at(var1, val_dict, order=1) \
                                   * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2
            else:
                return Cos.derivative_at(sub_expr1, (var,var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return -np.sin(sub_expr1.val)
        
def cos(expr):
    return Expression(Cos, expr)
    
class Tan:
    """ 
    This is a class to wrap up static method related to tan operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute tan of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        tan sub_expr1
        """
        return np.tan(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if   order == 1:
            return sub_expr1.derivative_at(var, val_dict) /(np.cos(sub_expr1.evaluation_at(val_dict))**2)
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                f = sub_expr1.evaluation_at(val_dict)
                term1 = 1/(np.cos(f)**2) * sub_expr1.derivative_at(var,  val_dict, order=2)
                term2 = 2*np.tan(f)/(np.cos(f)**2) \
                        * sub_expr1.derivative_at(var1, val_dict, order=1) \
                        * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2
            else:
                return Tan.derivative_at(sub_expr1, (var,var), val_dict, order=2)

        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return 1/(np.cos(sub_expr1.val)**2)

def tan(expr):
    return Expression(Tan, expr)
    
class Cotan:
    """ 
    This is a class to wrap up static method related to cotan operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute cotan of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        cotan sub_expr1
        """
        return 1/np.tan(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1): 
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if order == 1:
            return -sub_expr1.derivative_at(var, val_dict)/(np.sin(sub_expr1.evaluation_at(val_dict))**2)
        else: raise NotImplementedError('higher order derivatives not implemented for cotan.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return -1/(np.sin(sub_expr1.val)**2)          

def cotan(expr):
    return Expression(Cotan, expr)
    
class Sec:
    """ 
    This is a class to wrap up static method related to sec operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute sec of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sec sub_expr1
        """
        return 1/np.cos(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * \
               np.tan(x) * (1/np.cos(x))
        else: raise NotImplementedError('higher order derivatives not implemented for sec.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x =sub_expr1.val
        return np.tan(x)/np.cos(x)
        
def sec(expr):
    return Expression(Sec, expr) 

class Csc:
    """ 
    This is a class to wrap up static method related to csc operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute csc of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        csc sub_expr1
        """
        return 1/np.sin(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        if order == 1:
            return -sub_expr1.derivative_at(var, val_dict) * \
               (1/np.tan(x)) * (1/np.sin(x))
        else: raise NotImplementedError('higher order derivatives not implemented for csc.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return -(1/np.tan(x)) * (1/np.sin(x))

def csc(expr):
    return Expression(Csc, expr) 

class Sinh:
    """ 
    This is a class to wrap up static method related to sinh operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute sinh of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sinh sub_expr1
        """
        return np.sinh(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * np.cosh(x)
        else: raise NotImplementedError('higher order derivatives not implemented for sinh.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return np.cosh(x)

def sinh(expr):
    return Expression(Sinh, expr) 

class Cosh:
    """ 
    This is a class to wrap up static method related to cosh operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute cosh of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        cosh sub_expr1
        """
        return np.cosh(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * np.sinh(x)
        else: raise NotImplementedError('higher order derivatives not implemented for cosh.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return np.sinh(sub_expr1.val)

def cosh(expr):
    return Expression(Cosh, expr) 
    
class Tanh:
    """ 
    This is a class to wrap up static method related to tanh operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute tanh of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        tanh sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return np.sinh(x)/np.cosh(x)
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        tanh = np.sinh(x)/np.cosh(x)
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * (1-tanh*tanh)
        else: raise NotImplementedError('higher order derivatives not implemented for tanh.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        tanh = np.sinh(x)/np.cosh(x)
        return 1-tanh*tanh

def tanh(expr):
    return Expression(Tanh,expr) 

class Csch:
    """ 
    This is a class to wrap up static method related to csch operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute csch of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        csch sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return 1/np.sinh(x)
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        # d = -csch(x)*cot(x)
        d = -(1/np.sinh(x)) * (np.cosh(x)/np.sinh(x))
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * d
        else: raise NotImplementedError('higher order derivatives not implemented for csch.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return -(np.cosh(x)/np.sinh(x))*(1/np.sinh(x))

def csch(expr):
    return Expression(Csch, expr) 

class Sech:
    """ 
    This is a class to wrap up static method related to sech operation
    """
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute sech of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sech sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return 1/np.cosh(x)
    
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        # d = -sech(x)tanh(x)
        d = -(1/np.cosh(x)) * (np.sinh(x)/np.cosh(x))
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict)*d
        else: raise NotImplementedError('higher order derivatives not implemented for sech.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return -(1/np.cosh(x)) * (np.sinh(x)/np.cosh(x))

def sech(expr):
    return Expression(Sech, expr) 

class Coth:
    """ 
    This is a class to wrap up static method related to coth operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute coth of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        coth sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return np.cosh(x)/np.sinh(x)
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        coth = np.cosh(x)/np.sinh(x)

        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * (1-coth**2)
        else: raise NotImplementedError('higher order derivatives not implemented for cotan.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        coth = np.cosh(x)/np.sinh(x)            
        return 1-coth**2

def coth(expr):
    return Expression(Coth, expr)    

class Arcsin:
    """ 
    This is a class to wrap up static method related to arcsin operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute arcsin of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        arcsin sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return np.arcsin(x)
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        d = 1/np.sqrt(1-x**2)
        #1/sqrt(1-x^2)
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * d
        else: raise NotImplementedError('higher order derivatives not implemented for arcsin.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return 1/np.sqrt(1-x**2)

def arcsin(expr):
    return Expression(Arcsin, expr)
    
class Arccos:
    """ 
    This is a class to wrap up static method related to arccos operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute arccos of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        arccos sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return np.arccos(x)
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        d = 1/np.sqrt(1-x**2)
        #-1/sqrt(1-x^2)
        if order == 1:
            return -sub_expr1.derivative_at(var, val_dict) * d
        else: raise NotImplementedError('higher order derivatives not implemented for arccos.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return -1/np.sqrt(1-x**2)

def arccos(expr):
    return Expression(Arccos, expr)
    
class Arctan:
    """ 
    This is a class to wrap up static method related to arctan operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute arctan of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        arctan sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return np.arctan(x)
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        d = 1/(1+x**2)
        # d = 1/(1+x**2)
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * d
        else: raise NotImplementedError('higher order derivatives not implemented for arctan.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return 1/(1+x**2)

def arctan(expr):
    return Expression(Arctan, expr)

def logit(expr):
    return log(expr/(1-expr))

def sigmoid(expr):
    return 1/(1+exp(-expr))

Functions

def arccos(

expr)

def arccos(expr):
    return Expression(Arccos, expr)

def arcsin(

expr)

def arcsin(expr):
    return Expression(Arcsin, expr)

def arctan(

expr)

def arctan(expr):
    return Expression(Arctan, expr)

def cos(

expr)

def cos(expr):
    return Expression(Cos, expr)

def cosh(

expr)

def cosh(expr):
    return Expression(Cosh, expr) 

def cotan(

expr)

def cotan(expr):
    return Expression(Cotan, expr)

def coth(

expr)

def coth(expr):
    return Expression(Coth, expr)    

def csc(

expr)

def csc(expr):
    return Expression(Csc, expr) 

def csch(

expr)

def csch(expr):
    return Expression(Csch, expr) 

def exp(

expr)

def exp(expr):
    return Expression(Exp, expr)

def log(

expr)

def log(expr):
    return Expression(Log, expr)

def logit(

expr)

def logit(expr):
    return log(expr/(1-expr))

def power(

expr, p)

def power(expr, p):
    return Expression(Pow, expr, Constant(p))

def sec(

expr)

def sec(expr):
    return Expression(Sec, expr) 

def sech(

expr)

def sech(expr):
    return Expression(Sech, expr) 

def sigmoid(

expr)

def sigmoid(expr):
    return 1/(1+exp(-expr))

def sin(

expr)

def sin(expr):
    return Expression(Sin, expr)

def sinh(

expr)

def sinh(expr):
    return Expression(Sinh, expr) 

def sqrt(

expr)

def sqrt(expr):
    return Expression(Pow, expr, Constant(0.5))

def tan(

expr)

def tan(expr):
    return Expression(Tan, expr)

def tanh(

expr)

def tanh(expr):
    return Expression(Tanh,expr) 

Classes

class Add

This is a class to wrap up static method related to add operation

class Add:
    """ 
    This is a class to wrap up static method related to add operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, sub_expr2, val_dict):
        """
        Compute addition of sub_expr1 with sub_expr2 using inputs of variable
        values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sub_expr1 + sub_expr2
        """
        return sub_expr1.evaluation_at(val_dict) + \
               sub_expr2.evaluation_at(val_dict)
    @staticmethod
    def derivative_at(sub_expr1, sub_expr2, var, val_dict, order=1):
        return sub_expr1.derivative_at(var, val_dict, order) + \
               sub_expr2.derivative_at(var, val_dict, order)
    @staticmethod
    def backderivative_at(sub_expr1,sub_expr2,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant 
        sub_expr2: expression or constant 
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return 1

Ancestors (in MRO)

  • Add
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, sub_expr2, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression or constant sub_expr2: expression or constant var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,sub_expr2,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression or constant 
    sub_expr2: expression or constant 
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    return 1

def derivative_at(

sub_expr1, sub_expr2, var, val_dict, order=1)

@staticmethod
def derivative_at(sub_expr1, sub_expr2, var, val_dict, order=1):
    return sub_expr1.derivative_at(var, val_dict, order) + \
           sub_expr2.derivative_at(var, val_dict, order)

def evaluation_at(

sub_expr1, sub_expr2, val_dict)

Compute addition of sub_expr1 with sub_expr2 using inputs of variable values from val_dict.

INPUTS

sub_expr1: expression or constant sub_expr2: expression or constant val_dict: a dictionary containing variable name and values.

RETURNS

sub_expr1 + sub_expr2

@staticmethod
def evaluation_at(sub_expr1, sub_expr2, val_dict):
    """
    Compute addition of sub_expr1 with sub_expr2 using inputs of variable
    values from val_dict.

    INPUTS
    =======
    sub_expr1: expression or constant
    sub_expr2: expression or constant
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    sub_expr1 + sub_expr2
    """
    return sub_expr1.evaluation_at(val_dict) + \
           sub_expr2.evaluation_at(val_dict)

class Arccos

This is a class to wrap up static method related to arccos operation

class Arccos:
    """ 
    This is a class to wrap up static method related to arccos operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute arccos of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        arccos sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return np.arccos(x)
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        d = 1/np.sqrt(1-x**2)
        #-1/sqrt(1-x^2)
        if order == 1:
            return -sub_expr1.derivative_at(var, val_dict) * d
        else: raise NotImplementedError('higher order derivatives not implemented for arccos.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return -1/np.sqrt(1-x**2)

Ancestors (in MRO)

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.val
    return -1/np.sqrt(1-x**2)

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1,var,val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    d = 1/np.sqrt(1-x**2)
    #-1/sqrt(1-x^2)
    if order == 1:
        return -sub_expr1.derivative_at(var, val_dict) * d
    else: raise NotImplementedError('higher order derivatives not implemented for arccos.')

def evaluation_at(

sub_expr1, val_dict)

Compute arccos of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

arccos sub_expr1

@staticmethod
def evaluation_at(sub_expr1,val_dict):
    """
    Compute arccos of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    arccos sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    return np.arccos(x)

class Arcsin

This is a class to wrap up static method related to arcsin operation

class Arcsin:
    """ 
    This is a class to wrap up static method related to arcsin operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute arcsin of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        arcsin sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return np.arcsin(x)
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        d = 1/np.sqrt(1-x**2)
        #1/sqrt(1-x^2)
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * d
        else: raise NotImplementedError('higher order derivatives not implemented for arcsin.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return 1/np.sqrt(1-x**2)

Ancestors (in MRO)

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.val
    return 1/np.sqrt(1-x**2)

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1,var,val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    d = 1/np.sqrt(1-x**2)
    #1/sqrt(1-x^2)
    if order == 1:
        return sub_expr1.derivative_at(var, val_dict) * d
    else: raise NotImplementedError('higher order derivatives not implemented for arcsin.')

def evaluation_at(

sub_expr1, val_dict)

Compute arcsin of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

arcsin sub_expr1

@staticmethod
def evaluation_at(sub_expr1,val_dict):
    """
    Compute arcsin of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    arcsin sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    return np.arcsin(x)

class Arctan

This is a class to wrap up static method related to arctan operation

class Arctan:
    """ 
    This is a class to wrap up static method related to arctan operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute arctan of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        arctan sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return np.arctan(x)
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        d = 1/(1+x**2)
        # d = 1/(1+x**2)
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * d
        else: raise NotImplementedError('higher order derivatives not implemented for arctan.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return 1/(1+x**2)

Ancestors (in MRO)

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.val
    return 1/(1+x**2)

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1,var,val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    d = 1/(1+x**2)
    # d = 1/(1+x**2)
    if order == 1:
        return sub_expr1.derivative_at(var, val_dict) * d
    else: raise NotImplementedError('higher order derivatives not implemented for arctan.')

def evaluation_at(

sub_expr1, val_dict)

Compute arctan of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

arctan sub_expr1

@staticmethod
def evaluation_at(sub_expr1,val_dict):
    """
    Compute arctan of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    arctan sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    return np.arctan(x)

class Constant

This is a class for representing constant.

Attributes: val: value of the constant

class Constant(Expression):
    """ 
    This is a class for representing constant. 
      
    Attributes: 
       val: value of the constant
    """
    def __init__(self, val):
        """ 
        The constructor for VectorFunction class. 
        
        PARAMETERS:
        =======
        val: the value of the constant object
        """
        self.val = val
 
    def evaluation_at(self, val_dict):
        """ 
        The function to evaluation the value of constant class
        
        PARAMETERS:
        ======= 
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        a scalar value 
        """
        return self.val
    
    def derivative_at(self, var, val_dict, order=1):
        """ 
        The function calculates derivative of constant class. 
  
        PARAMETERS:
        ======= 
        val_dict: a dictionary containing variable name and values.
        var: variable whose derivative is the result of this function
        order: default set to 1 for 1st derivative, change to 2 for 
        higher order
        
        RETURNS
        ========
        scalar value  
        """
        return 0.0
    
    def __eq__(self, another):
         """ Implement dunder method for equal """
         if isinstance(another, Constant): return True
         else:                             return False
    
    def __ne__(self, another):
         """ Implement dunder method for not equal """
         return ~self.__eq__(another)
    
    def __hash__(self):
         """ Implement dunder method for hash"""
         return Expression.__hash__(self) 

Ancestors (in MRO)

Static methods

def __init__(

self, val)

The constructor for VectorFunction class.

PARAMETERS:

val: the value of the constant object

def __init__(self, val):
    """ 
    The constructor for VectorFunction class. 
    
    PARAMETERS:
    =======
    val: the value of the constant object
    """
    self.val = val

def back_derivative(

self, var, val_dict)

The wrapper function for individual backderivative_at function of self_ele_func

PARAMETERS:

val_dict: a dictionary containing variable name and values. Variables in val_dict are of atomic feature and cannot be further decomposed. var: variable with respect to which the function calculates derivative

RETURNS

derivative of var with respect to the immediate parent that contain var

def back_derivative(self,var,val_dict):
    """
    The wrapper function for individual backderivative_at 
    function of self_ele_func
    
    PARAMETERS:
    =======
    val_dict: a dictionary containing variable name and values. Variables
    in val_dict are of atomic feature and cannot be further decomposed.
    var: variable with respect to which the function calculates derivative   
    
    RETURNS
    ========
    derivative of var with respect to the immediate parent that contain var
    """
    if var is self: return 1.0
    if self._sub_expr2 is None:
        return self._ele_func.backderivative_at(self._sub_expr1,var)
    else:
        return self._ele_func.backderivative_at(self._sub_expr1,
                                                self._sub_expr2,var)    

def derivative_at(

self, var, val_dict, order=1)

The function calculates derivative of constant class.

PARAMETERS:

val_dict: a dictionary containing variable name and values. var: variable whose derivative is the result of this function order: default set to 1 for 1st derivative, change to 2 for higher order

RETURNS

scalar value

def derivative_at(self, var, val_dict, order=1):
    """ 
    The function calculates derivative of constant class. 
    PARAMETERS:
    ======= 
    val_dict: a dictionary containing variable name and values.
    var: variable whose derivative is the result of this function
    order: default set to 1 for 1st derivative, change to 2 for 
    higher order
    
    RETURNS
    ========
    scalar value  
    """
    return 0.0

def evaluation_at(

self, val_dict)

The function to evaluation the value of constant class

PARAMETERS:

val_dict: a dictionary containing variable name and values.

RETURNS

a scalar value

def evaluation_at(self, val_dict):
    """ 
    The function to evaluation the value of constant class
    
    PARAMETERS:
    ======= 
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    a scalar value 
    """
    return self.val

def gradient_at(

self, val_dict, returns_dict=False)

calculate 1st derivative of variables in val_dict using forward mode

INPUTS

val_dict: a dictionary containing variable name and values. returns_dict: the format of output

RETURNS

derivative of variables in val_dict with respect to the current expression, stored in a dictionary or a 2-D numpy array

def gradient_at(self, val_dict, returns_dict=False):
    """
    calculate 1st derivative of variables in val_dict using forward mode

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    returns_dict: the format of output
     
    RETURNS
    ========
    derivative of variables in val_dict with respect to the current 
    expression, stored in a dictionary or a 2-D numpy array
    """
    if returns_dict:
        return {v: self.derivative_at(v, val_dict) for v in val_dict.keys()}
    return np.array([self.derivative_at(var, val_dict, order=1) 
                     for var in val_dict.keys()])

def hessian_at(

self, val_dict)

calculate 2nd derivative of variables in val_dict using forward mode

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

2nd derivative of variables in val_dict with respect to the current expression, stored in a 2-D list

def hessian_at(self, val_dict):
    """
    calculate 2nd derivative of variables in val_dict using forward mode

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
     
    RETURNS
    ========
    2nd derivative of variables in val_dict with respect to the current 
    expression, stored in a 2-D list
    """
    return np.array( [ \
                      [self.derivative_at((var1, var2), val_dict, order=2)
                       for var1 in val_dict.keys()]
                      for var2 in val_dict.keys() \
                      ] )

Instance variables

var val

Inheritance: Expression.val

class Cos

This is a class to wrap up static method related to cos operation

class Cos:
    """ 
    This is a class to wrap up static method related to cos operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute cos of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        cos sub_expr1
        """
        return np.cos(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if   order == 1:
            return -sub_expr1.derivative_at(var, val_dict, order) * \
                   np.sin(sub_expr1.evaluation_at(val_dict)) 
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                f = sub_expr1.evaluation_at(val_dict)
                term1 = -np.sin(f) * sub_expr1.derivative_at(var,  val_dict, order=2)
                term2 = -np.cos(f) * sub_expr1.derivative_at(var1, val_dict, order=1) \
                                   * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2
            else:
                return Cos.derivative_at(sub_expr1, (var,var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return -np.sin(sub_expr1.val)

Ancestors (in MRO)

  • Cos
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    return -np.sin(sub_expr1.val)

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression or constant val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1,var,val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression or constant
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    if   order == 1:
        return -sub_expr1.derivative_at(var, val_dict, order) * \
               np.sin(sub_expr1.evaluation_at(val_dict)) 
    elif order == 2:
        if type(var) is tuple:
            var1, var2 = var
            f = sub_expr1.evaluation_at(val_dict)
            term1 = -np.sin(f) * sub_expr1.derivative_at(var,  val_dict, order=2)
            term2 = -np.cos(f) * sub_expr1.derivative_at(var1, val_dict, order=1) \
                               * sub_expr1.derivative_at(var2, val_dict, order=1)
            return term1 + term2
        else:
            return Cos.derivative_at(sub_expr1, (var,var), val_dict, order=2)
    else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')

def evaluation_at(

sub_expr1, val_dict)

Compute cos of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

cos sub_expr1

@staticmethod
def evaluation_at(sub_expr1,val_dict):
    """
    Compute cos of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    cos sub_expr1
    """
    return np.cos(sub_expr1.evaluation_at(val_dict))

class Cosh

This is a class to wrap up static method related to cosh operation

class Cosh:
    """ 
    This is a class to wrap up static method related to cosh operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute cosh of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        cosh sub_expr1
        """
        return np.cosh(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * np.sinh(x)
        else: raise NotImplementedError('higher order derivatives not implemented for cosh.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return np.sinh(sub_expr1.val)

Ancestors (in MRO)

  • Cosh
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    return np.sinh(sub_expr1.val)

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1,var,val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    if order == 1:
        return sub_expr1.derivative_at(var, val_dict) * np.sinh(x)
    else: raise NotImplementedError('higher order derivatives not implemented for cosh.')

def evaluation_at(

sub_expr1, val_dict)

Compute cosh of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

cosh sub_expr1

@staticmethod
def evaluation_at(sub_expr1,val_dict):
    """
    Compute cosh of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    cosh sub_expr1
    """
    return np.cosh(sub_expr1.evaluation_at(val_dict))

class Cotan

This is a class to wrap up static method related to cotan operation

class Cotan:
    """ 
    This is a class to wrap up static method related to cotan operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute cotan of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        cotan sub_expr1
        """
        return 1/np.tan(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1): 
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if order == 1:
            return -sub_expr1.derivative_at(var, val_dict)/(np.sin(sub_expr1.evaluation_at(val_dict))**2)
        else: raise NotImplementedError('higher order derivatives not implemented for cotan.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return -1/(np.sin(sub_expr1.val)**2)          

Ancestors (in MRO)

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    return -1/(np.sin(sub_expr1.val)**2)          

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1,var,val_dict, order=1): 
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    if order == 1:
        return -sub_expr1.derivative_at(var, val_dict)/(np.sin(sub_expr1.evaluation_at(val_dict))**2)
    else: raise NotImplementedError('higher order derivatives not implemented for cotan.')

def evaluation_at(

sub_expr1, val_dict)

Compute cotan of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

cotan sub_expr1

@staticmethod
def evaluation_at(sub_expr1,val_dict):
    """
    Compute cotan of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    cotan sub_expr1
    """
    return 1/np.tan(sub_expr1.evaluation_at(val_dict))

class Coth

This is a class to wrap up static method related to coth operation

class Coth:
    """ 
    This is a class to wrap up static method related to coth operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute coth of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        coth sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return np.cosh(x)/np.sinh(x)
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        coth = np.cosh(x)/np.sinh(x)

        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * (1-coth**2)
        else: raise NotImplementedError('higher order derivatives not implemented for cotan.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        coth = np.cosh(x)/np.sinh(x)            
        return 1-coth**2

Ancestors (in MRO)

  • Coth
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.val
    coth = np.cosh(x)/np.sinh(x)            
    return 1-coth**2

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1,var,val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    coth = np.cosh(x)/np.sinh(x)
    if order == 1:
        return sub_expr1.derivative_at(var, val_dict) * (1-coth**2)
    else: raise NotImplementedError('higher order derivatives not implemented for cotan.')

def evaluation_at(

sub_expr1, val_dict)

Compute coth of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

coth sub_expr1

@staticmethod
def evaluation_at(sub_expr1,val_dict):
    """
    Compute coth of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    coth sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    return np.cosh(x)/np.sinh(x)

class Csc

This is a class to wrap up static method related to csc operation

class Csc:
    """ 
    This is a class to wrap up static method related to csc operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute csc of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        csc sub_expr1
        """
        return 1/np.sin(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        if order == 1:
            return -sub_expr1.derivative_at(var, val_dict) * \
               (1/np.tan(x)) * (1/np.sin(x))
        else: raise NotImplementedError('higher order derivatives not implemented for csc.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return -(1/np.tan(x)) * (1/np.sin(x))

Ancestors (in MRO)

  • Csc
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.val
    return -(1/np.tan(x)) * (1/np.sin(x))

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1,var,val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    if order == 1:
        return -sub_expr1.derivative_at(var, val_dict) * \
           (1/np.tan(x)) * (1/np.sin(x))
    else: raise NotImplementedError('higher order derivatives not implemented for csc.')

def evaluation_at(

sub_expr1, val_dict)

Compute csc of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

csc sub_expr1

@staticmethod
def evaluation_at(sub_expr1,val_dict):
    """
    Compute csc of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    csc sub_expr1
    """
    return 1/np.sin(sub_expr1.evaluation_at(val_dict))

class Csch

This is a class to wrap up static method related to csch operation

class Csch:
    """ 
    This is a class to wrap up static method related to csch operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute csch of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        csch sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return 1/np.sinh(x)
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        # d = -csch(x)*cot(x)
        d = -(1/np.sinh(x)) * (np.cosh(x)/np.sinh(x))
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * d
        else: raise NotImplementedError('higher order derivatives not implemented for csch.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return -(np.cosh(x)/np.sinh(x))*(1/np.sinh(x))

Ancestors (in MRO)

  • Csch
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.val
    return -(np.cosh(x)/np.sinh(x))*(1/np.sinh(x))

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1,var,val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    # d = -csch(x)*cot(x)
    d = -(1/np.sinh(x)) * (np.cosh(x)/np.sinh(x))
    if order == 1:
        return sub_expr1.derivative_at(var, val_dict) * d
    else: raise NotImplementedError('higher order derivatives not implemented for csch.')

def evaluation_at(

sub_expr1, val_dict)

Compute csch of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

csch sub_expr1

@staticmethod
def evaluation_at(sub_expr1,val_dict):
    """
    Compute csch of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    csch sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    return 1/np.sinh(x)

class Div

This is a class to wrap up static method related to div operation

class Div:
    """ 
    This is a class to wrap up static method related to div operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, sub_expr2, val_dict):
        """
        Compute division of sub_expr1 by sub_expr2 using inputs of variable
        values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sub_expr1 / sub_expr2
        """
        return sub_expr1.evaluation_at(val_dict) /\
               sub_expr2.evaluation_at(val_dict)
    @staticmethod
    def derivative_at(sub_expr1, sub_expr2, var, val_dict,order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if   order == 1:
            return  sub_expr1.derivative_at(var, val_dict) / \
                    sub_expr2.evaluation_at(val_dict)- \
                    sub_expr1.evaluation_at(val_dict) *\
                    sub_expr2.derivative_at(var, val_dict)/\
                    sub_expr2.evaluation_at(val_dict)**2
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                f = sub_expr1.evaluation_at(val_dict)
                g = sub_expr2.evaluation_at(val_dict)
                term1 =  1/g    * sub_expr2.derivative_at(var, val_dict, order=2)
                term2 = -f/g**2 * sub_expr1.derivative_at(var, val_dict, order=2)
                term3 = -1/g**2 * sub_expr1.derivative_at(var1, val_dict, order=1) \
                                * sub_expr2.derivative_at(var2, val_dict, order=1)
                term4 = -1/g**2 * sub_expr1.derivative_at(var2, val_dict, order=1) \
                                * sub_expr2.derivative_at(var1, val_dict, order=1)
                term5 = 2*f/g**3 * sub_expr2.derivative_at(var1, val_dict, order=1) \
                                 * sub_expr2.derivative_at(var2, val_dict, order=1)
                return term1 + term2 + term3 + term4 + term5  
            else:
                return Div.derivative_at(sub_expr1, sub_expr2, (var, var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,sub_expr2,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant 
        sub_expr2: expression or constant 
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if var == sub_expr1:
            return 1/sub_expr2.val
        elif var == sub_expr2:
            return -sub_expr1.val/sub_expr2.val**2

Ancestors (in MRO)

  • Div
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, sub_expr2, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression or constant sub_expr2: expression or constant var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,sub_expr2,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression or constant 
    sub_expr2: expression or constant 
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    if var == sub_expr1:
        return 1/sub_expr2.val
    elif var == sub_expr2:
        return -sub_expr1.val/sub_expr2.val**2

def derivative_at(

sub_expr1, sub_expr2, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression or constant sub_expr2: expression or constant val_dict: a dictionary containing variable name and values. var: variable of interest order: default set to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1, sub_expr2, var, val_dict,order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression or constant
    sub_expr2: expression or constant
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default set to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    if   order == 1:
        return  sub_expr1.derivative_at(var, val_dict) / \
                sub_expr2.evaluation_at(val_dict)- \
                sub_expr1.evaluation_at(val_dict) *\
                sub_expr2.derivative_at(var, val_dict)/\
                sub_expr2.evaluation_at(val_dict)**2
    elif order == 2:
        if type(var) is tuple:
            var1, var2 = var
            f = sub_expr1.evaluation_at(val_dict)
            g = sub_expr2.evaluation_at(val_dict)
            term1 =  1/g    * sub_expr2.derivative_at(var, val_dict, order=2)
            term2 = -f/g**2 * sub_expr1.derivative_at(var, val_dict, order=2)
            term3 = -1/g**2 * sub_expr1.derivative_at(var1, val_dict, order=1) \
                            * sub_expr2.derivative_at(var2, val_dict, order=1)
            term4 = -1/g**2 * sub_expr1.derivative_at(var2, val_dict, order=1) \
                            * sub_expr2.derivative_at(var1, val_dict, order=1)
            term5 = 2*f/g**3 * sub_expr2.derivative_at(var1, val_dict, order=1) \
                             * sub_expr2.derivative_at(var2, val_dict, order=1)
            return term1 + term2 + term3 + term4 + term5  
        else:
            return Div.derivative_at(sub_expr1, sub_expr2, (var, var), val_dict, order=2)
    else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')

def evaluation_at(

sub_expr1, sub_expr2, val_dict)

Compute division of sub_expr1 by sub_expr2 using inputs of variable values from val_dict.

INPUTS

sub_expr1: expression or constant sub_expr2: expression or constant val_dict: a dictionary containing variable name and values.

RETURNS

sub_expr1 / sub_expr2

@staticmethod
def evaluation_at(sub_expr1, sub_expr2, val_dict):
    """
    Compute division of sub_expr1 by sub_expr2 using inputs of variable
    values from val_dict.

    INPUTS
    =======
    sub_expr1: expression or constant
    sub_expr2: expression or constant
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    sub_expr1 / sub_expr2
    """
    return sub_expr1.evaluation_at(val_dict) /\
           sub_expr2.evaluation_at(val_dict)

class Exp

This is a class to wrap up static method related to exp operation

class Exp:
    """ 
    This is a class to wrap up static method related to exp operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, val_dict):
        """
        Compute exponent of sub_expr1 using inputs of variable
        values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        exponent(sub_expr1)
        """
        return np.exp(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1, var, val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if   order == 1:
            return sub_expr1.derivative_at(var, val_dict) * \
                   np.exp(sub_expr1.evaluation_at(val_dict))
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                f = sub_expr1.evaluation_at(val_dict)
                term1 = np.exp(f) * sub_expr1.derivative_at(var,  val_dict, order=2)
                term2 = np.exp(f) * sub_expr1.derivative_at(var1, val_dict, order=1) \
                                  * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2
            else:
                return Exp.derivative_at(sub_expr1, (var,var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return np.exp(sub_expr1.val)

Ancestors (in MRO)

  • Exp
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression or constant var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression or constant
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    return np.exp(sub_expr1.val)

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default set to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1, var, val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default set to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    if   order == 1:
        return sub_expr1.derivative_at(var, val_dict) * \
               np.exp(sub_expr1.evaluation_at(val_dict))
    elif order == 2:
        if type(var) is tuple:
            var1, var2 = var
            f = sub_expr1.evaluation_at(val_dict)
            term1 = np.exp(f) * sub_expr1.derivative_at(var,  val_dict, order=2)
            term2 = np.exp(f) * sub_expr1.derivative_at(var1, val_dict, order=1) \
                              * sub_expr1.derivative_at(var2, val_dict, order=1)
            return term1 + term2
        else:
            return Exp.derivative_at(sub_expr1, (var,var), val_dict, order=2)
    else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')

def evaluation_at(

sub_expr1, val_dict)

Compute exponent of sub_expr1 using inputs of variable values from val_dict.

INPUTS

sub_expr1: expression or constant val_dict: a dictionary containing variable name and values.

RETURNS

exponent(sub_expr1)

@staticmethod
def evaluation_at(sub_expr1, val_dict):
    """
    Compute exponent of sub_expr1 using inputs of variable
    values from val_dict.

    INPUTS
    =======
    sub_expr1: expression or constant
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    exponent(sub_expr1)
    """
    return np.exp(sub_expr1.evaluation_at(val_dict))

class Expression

This is a class for representing expression. It is the super class for variable and constant.

class Expression:
    """ 
    This is a class for representing expression.
    It is the super class for variable and constant.
    """
    def __init__(self, ele_func, sub_expr1, sub_expr2=None):
        """ 
        The constructor for VectorFunction class. 
        
        PARAMETERS:
        =======
        ele_func: the function creating this expression
        sub_expr1: variable/constant composing this expression
        sub_expr2: variable/constant composing this expression, set to non
        for unary operations
        """
        self._ele_func  = ele_func
        self._sub_expr1 = sub_expr1
        self._sub_expr2 = sub_expr2
        self.val = None
        self.bder=0
    
    def evaluation_at(self, val_dict):
        """ 
        The wrapper function for individual evaluation_at function of 
        self_ele_func
        
        PARAMETERS:
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        a scalar value 
        """
        # self._sub_expr2 is None implies that self._ele_func is an unary operator
        if self._sub_expr2 is None: 
            return self._ele_func.evaluation_at(
                self._sub_expr1, val_dict)
        
        # self._sub_expr2 not None implies that self._ele_func is a binary operator
        else:
            return self._ele_func.evaluation_at(
                self._sub_expr1, self._sub_expr2, val_dict)
    
    def derivative_at(self, var, val_dict, order=1):
        """ 
        The wrapper function for individual derivative_at function of 
        self_ele_func
        
        PARAMETERS:
        =======
        val_dict: a dictionary containing variable name and values.
        var: variable of interests for derivative calculation
        
        RETURNS
        ======== 
        a scalar value 
        """
        
        if type(var) is tuple: order=len(var)
        if var is self: 
            if   order == 1: return 1.0
            else: return 0.0
        
        # sub_expr2 being None implies that _ele_func is an unary operator
        if self._sub_expr2 is None:
            return self._ele_func.derivative_at(
                self._sub_expr1, var, val_dict, order)
        
        # sub_expr2 not None implies that _ele_func is a binary operator
        else:
            return self._ele_func.derivative_at(
                self._sub_expr1, self._sub_expr2, var, val_dict, order)
    
    def back_derivative(self,var,val_dict):
        """
        The wrapper function for individual backderivative_at 
        function of self_ele_func
        
        PARAMETERS:
        =======
        val_dict: a dictionary containing variable name and values. Variables
        in val_dict are of atomic feature and cannot be further decomposed.
        var: variable with respect to which the function calculates derivative   
        
        RETURNS
        ========
        derivative of var with respect to the immediate parent that contain var
        """
        if var is self: return 1.0
        if self._sub_expr2 is None:
            return self._ele_func.backderivative_at(self._sub_expr1,var)
        else:
            return self._ele_func.backderivative_at(self._sub_expr1,
                                                    self._sub_expr2,var)    



    def gradient_at(self, val_dict, returns_dict=False):
        """
        calculate 1st derivative of variables in val_dict using forward mode
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        returns_dict: the format of output
         
        RETURNS
        ========
        derivative of variables in val_dict with respect to the current 
        expression, stored in a dictionary or a 2-D numpy array
        """
        if returns_dict:
            return {v: self.derivative_at(v, val_dict) for v in val_dict.keys()}
        return np.array([self.derivative_at(var, val_dict, order=1) 
                         for var in val_dict.keys()])
    
    def hessian_at(self, val_dict):
        """
        calculate 2nd derivative of variables in val_dict using forward mode
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
         
        RETURNS
        ========
        2nd derivative of variables in val_dict with respect to the current 
        expression, stored in a 2-D list
        """
        return np.array( [ \
                          [self.derivative_at((var1, var2), val_dict, order=2)
                           for var1 in val_dict.keys()]
                          for var2 in val_dict.keys() \
                          ] )
    
    def __neg__(self):
        """ Implement dunder method for neg """
        return Expression(Neg, self)

                
    def __add__(self, another):
        """ Implement dunder method for add """
        if isinstance(another, Expression):
            return Expression(Add, self, another)
        # if the other operand is not an Expression, then it must be a number
        # the number then should be converted to a Constant
        else:
            return Expression(Add, self, Constant(another))
    
    
    def __radd__(self, another):
        """ Implement dunder method for right add """
        if isinstance(another, Expression):
            return Expression(Add, another, self)
        else:
            return Expression(Add, Constant(another), self)
    
    def __sub__(self, another):
        """ Implement dunder method for subtraction """
        if isinstance(another, Expression):
            return Expression(Sub, self, another)
        else:
            return Expression(Sub, self, Constant(another))
    
    def __rsub__(self, another):
        """ Implement dunder method for right subtraction """
        if isinstance(another, Expression):
            return Expression(Sub, another, self)
        else:
            return Expression(Sub, Constant(another), self)
        

    def __mul__(self, another):
        """ Implement dunder method for multiplication """
        if isinstance(another, Expression):
            return Expression(Mul,self,another)
        else:
            return Expression(Mul, self, Constant(another))

    def __rmul__(self, another):
        """ Implement dunder method for right multiplication """
        if isinstance(another, Expression):
            return Expression(Mul,another,self)
        else:
            return Expression(Mul, Constant(another),self)
    
    def __truediv__(self, another):
        """ Implement dunder method for division """
        if isinstance(another, Expression):
            return Expression(Div,self,another)
        else:
            return Expression(Div, self, Constant(another))

    def __rtruediv__(self, another):
        """ Implement dunder method for right division """
        if isinstance(another, Expression):
            return Expression(Div,another,self)
        else:
            return Expression(Div, Constant(another),self)
    
    def __pow__(self,another):
        """ Implement dunder method for power """
        if isinstance(another, Expression):
            return Expression(Pow,self,another)
        else:
            return Expression(Pow, self, Constant(another))
    
    def __rpow__(self,another):
        """ Implement dunder method for right power """
        if isinstance(another, Expression):
            return Expression(Pow,another,self)
        else:
            return Expression(Pow, Constant(another),self)
    
    def __eq__(self, another):
        """ Implement dunder method for equal """
        if not isinstance(another, Expression):
            return False
        return self._ele_func == another._ele_func \
               and self._sub_expr1 == another._sub_expr1 \
               and self._sub_expr2 == another._sub_expr2
               
    def __ne__(self, another):
        """ Implement dunder method not equal """
        return ~self.__eq__(another)
    
    def __hash__(self):
        """ Implement dunder method hash """
        return object.__hash__(self)   

Ancestors (in MRO)

Static methods

def __init__(

self, ele_func, sub_expr1, sub_expr2=None)

The constructor for VectorFunction class.

PARAMETERS:

ele_func: the function creating this expression sub_expr1: variable/constant composing this expression sub_expr2: variable/constant composing this expression, set to non for unary operations

def __init__(self, ele_func, sub_expr1, sub_expr2=None):
    """ 
    The constructor for VectorFunction class. 
    
    PARAMETERS:
    =======
    ele_func: the function creating this expression
    sub_expr1: variable/constant composing this expression
    sub_expr2: variable/constant composing this expression, set to non
    for unary operations
    """
    self._ele_func  = ele_func
    self._sub_expr1 = sub_expr1
    self._sub_expr2 = sub_expr2
    self.val = None
    self.bder=0

def back_derivative(

self, var, val_dict)

The wrapper function for individual backderivative_at function of self_ele_func

PARAMETERS:

val_dict: a dictionary containing variable name and values. Variables in val_dict are of atomic feature and cannot be further decomposed. var: variable with respect to which the function calculates derivative

RETURNS

derivative of var with respect to the immediate parent that contain var

def back_derivative(self,var,val_dict):
    """
    The wrapper function for individual backderivative_at 
    function of self_ele_func
    
    PARAMETERS:
    =======
    val_dict: a dictionary containing variable name and values. Variables
    in val_dict are of atomic feature and cannot be further decomposed.
    var: variable with respect to which the function calculates derivative   
    
    RETURNS
    ========
    derivative of var with respect to the immediate parent that contain var
    """
    if var is self: return 1.0
    if self._sub_expr2 is None:
        return self._ele_func.backderivative_at(self._sub_expr1,var)
    else:
        return self._ele_func.backderivative_at(self._sub_expr1,
                                                self._sub_expr2,var)    

def derivative_at(

self, var, val_dict, order=1)

The wrapper function for individual derivative_at function of self_ele_func

PARAMETERS:

val_dict: a dictionary containing variable name and values. var: variable of interests for derivative calculation

RETURNS

a scalar value

def derivative_at(self, var, val_dict, order=1):
    """ 
    The wrapper function for individual derivative_at function of 
    self_ele_func
    
    PARAMETERS:
    =======
    val_dict: a dictionary containing variable name and values.
    var: variable of interests for derivative calculation
    
    RETURNS
    ======== 
    a scalar value 
    """
    
    if type(var) is tuple: order=len(var)
    if var is self: 
        if   order == 1: return 1.0
        else: return 0.0
    
    # sub_expr2 being None implies that _ele_func is an unary operator
    if self._sub_expr2 is None:
        return self._ele_func.derivative_at(
            self._sub_expr1, var, val_dict, order)
    
    # sub_expr2 not None implies that _ele_func is a binary operator
    else:
        return self._ele_func.derivative_at(
            self._sub_expr1, self._sub_expr2, var, val_dict, order)

def evaluation_at(

self, val_dict)

The wrapper function for individual evaluation_at function of self_ele_func

PARAMETERS:

val_dict: a dictionary containing variable name and values.

RETURNS

a scalar value

def evaluation_at(self, val_dict):
    """ 
    The wrapper function for individual evaluation_at function of 
    self_ele_func
    
    PARAMETERS:
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    a scalar value 
    """
    # self._sub_expr2 is None implies that self._ele_func is an unary operator
    if self._sub_expr2 is None: 
        return self._ele_func.evaluation_at(
            self._sub_expr1, val_dict)
    
    # self._sub_expr2 not None implies that self._ele_func is a binary operator
    else:
        return self._ele_func.evaluation_at(
            self._sub_expr1, self._sub_expr2, val_dict)

def gradient_at(

self, val_dict, returns_dict=False)

calculate 1st derivative of variables in val_dict using forward mode

INPUTS

val_dict: a dictionary containing variable name and values. returns_dict: the format of output

RETURNS

derivative of variables in val_dict with respect to the current expression, stored in a dictionary or a 2-D numpy array

def gradient_at(self, val_dict, returns_dict=False):
    """
    calculate 1st derivative of variables in val_dict using forward mode

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    returns_dict: the format of output
     
    RETURNS
    ========
    derivative of variables in val_dict with respect to the current 
    expression, stored in a dictionary or a 2-D numpy array
    """
    if returns_dict:
        return {v: self.derivative_at(v, val_dict) for v in val_dict.keys()}
    return np.array([self.derivative_at(var, val_dict, order=1) 
                     for var in val_dict.keys()])

def hessian_at(

self, val_dict)

calculate 2nd derivative of variables in val_dict using forward mode

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

2nd derivative of variables in val_dict with respect to the current expression, stored in a 2-D list

def hessian_at(self, val_dict):
    """
    calculate 2nd derivative of variables in val_dict using forward mode

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
     
    RETURNS
    ========
    2nd derivative of variables in val_dict with respect to the current 
    expression, stored in a 2-D list
    """
    return np.array( [ \
                      [self.derivative_at((var1, var2), val_dict, order=2)
                       for var1 in val_dict.keys()]
                      for var2 in val_dict.keys() \
                      ] )

Instance variables

var bder

var val

class Log

class Log:
    
    @staticmethod
    def evaluation_at(sub_expr1, val_dict):
        return np.log(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1, var, val_dict, order=1):
        if   order == 1:
            return 1 / sub_expr1.evaluation_at(val_dict) * sub_expr1.derivative_at(var, val_dict)
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                f = sub_expr1.evaluation_at(val_dict)
                term1 = 1/f * sub_expr1.derivative_at(var,  val_dict, order=2)
                term2 = -1/f**2 * sub_expr1.derivative_at(var1, val_dict, order=1) \
                            * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2
            else:
                return Log.derivative_at(sub_expr1, (var,var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    def backderivative_at(sub_expr1,var):
        if sub_expr1 == var:
            return 1/sub_expr1.val

Ancestors (in MRO)

  • Log
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

def backderivative_at(sub_expr1,var):
    if sub_expr1 == var:
        return 1/sub_expr1.val

def derivative_at(

sub_expr1, var, val_dict, order=1)

@staticmethod
def derivative_at(sub_expr1, var, val_dict, order=1):
    if   order == 1:
        return 1 / sub_expr1.evaluation_at(val_dict) * sub_expr1.derivative_at(var, val_dict)
    elif order == 2:
        if type(var) is tuple:
            var1, var2 = var
            f = sub_expr1.evaluation_at(val_dict)
            term1 = 1/f * sub_expr1.derivative_at(var,  val_dict, order=2)
            term2 = -1/f**2 * sub_expr1.derivative_at(var1, val_dict, order=1) \
                        * sub_expr1.derivative_at(var2, val_dict, order=1)
            return term1 + term2
        else:
            return Log.derivative_at(sub_expr1, (var,var), val_dict, order=2)
    else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')

def evaluation_at(

sub_expr1, val_dict)

@staticmethod
def evaluation_at(sub_expr1, val_dict):
    return np.log(sub_expr1.evaluation_at(val_dict))

class Mul

This is a class to wrap up static method related to mul operation

class Mul:
    """ 
    This is a class to wrap up static method related to mul operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, sub_expr2, val_dict):
        """
        Compute multiplication of sub_expr1 with sub_expr2 using inputs 
        of variable values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sub_expr1 * sub_expr2
        """
        return sub_expr1.evaluation_at(val_dict) *\
               sub_expr2.evaluation_at(val_dict)
    @staticmethod
    def derivative_at(sub_expr1, sub_expr2, var, val_dict,order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if   order == 1:
            return sub_expr1.derivative_at(var, val_dict) * \
                   sub_expr2.evaluation_at(val_dict)+ \
                   sub_expr1.evaluation_at(val_dict) *\
                   sub_expr2.derivative_at(var, val_dict)
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                term1 = sub_expr1.derivative_at(var, val_dict, order=2) \
                        * sub_expr2.evaluation_at(val_dict)
                term2 = sub_expr2.derivative_at(var, val_dict, order=2) \
                        * sub_expr1.evaluation_at(val_dict)
                term3 = sub_expr1.derivative_at(var1, val_dict, order=1) \
                        * sub_expr2.derivative_at(var2, val_dict, order=1)
                term4 = sub_expr2.derivative_at(var1, val_dict, order=1) \
                        * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2 + term3 + term4
            else:
                return Mul.derivative_at(sub_expr1, sub_expr2, (var, var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,sub_expr2,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant 
        sub_expr2: expression or constant 
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if var == sub_expr1:
            return sub_expr2.val
        else:
            return sub_expr1.val

Ancestors (in MRO)

  • Mul
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, sub_expr2, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression or constant sub_expr2: expression or constant var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,sub_expr2,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression or constant 
    sub_expr2: expression or constant 
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    if var == sub_expr1:
        return sub_expr2.val
    else:
        return sub_expr1.val

def derivative_at(

sub_expr1, sub_expr2, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression or constant sub_expr2: expression or constant val_dict: a dictionary containing variable name and values. var: variable of interest order: default set to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1, sub_expr2, var, val_dict,order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression or constant
    sub_expr2: expression or constant
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default set to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    if   order == 1:
        return sub_expr1.derivative_at(var, val_dict) * \
               sub_expr2.evaluation_at(val_dict)+ \
               sub_expr1.evaluation_at(val_dict) *\
               sub_expr2.derivative_at(var, val_dict)
    elif order == 2:
        if type(var) is tuple:
            var1, var2 = var
            term1 = sub_expr1.derivative_at(var, val_dict, order=2) \
                    * sub_expr2.evaluation_at(val_dict)
            term2 = sub_expr2.derivative_at(var, val_dict, order=2) \
                    * sub_expr1.evaluation_at(val_dict)
            term3 = sub_expr1.derivative_at(var1, val_dict, order=1) \
                    * sub_expr2.derivative_at(var2, val_dict, order=1)
            term4 = sub_expr2.derivative_at(var1, val_dict, order=1) \
                    * sub_expr1.derivative_at(var2, val_dict, order=1)
            return term1 + term2 + term3 + term4
        else:
            return Mul.derivative_at(sub_expr1, sub_expr2, (var, var), val_dict, order=2)
    else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')

def evaluation_at(

sub_expr1, sub_expr2, val_dict)

Compute multiplication of sub_expr1 with sub_expr2 using inputs of variable values from val_dict.

INPUTS

sub_expr1: expression or constant sub_expr2: expression or constant val_dict: a dictionary containing variable name and values.

RETURNS

sub_expr1 * sub_expr2

@staticmethod
def evaluation_at(sub_expr1, sub_expr2, val_dict):
    """
    Compute multiplication of sub_expr1 with sub_expr2 using inputs 
    of variable values from val_dict.

    INPUTS
    =======
    sub_expr1: expression or constant
    sub_expr2: expression or constant
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    sub_expr1 * sub_expr2
    """
    return sub_expr1.evaluation_at(val_dict) *\
           sub_expr2.evaluation_at(val_dict)

class Neg

This is a class to wrap up static method related to neg operation

class Neg:
    """ 
    This is a class to wrap up static method related to neg operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, val_dict):
        """
        Compute negation of sub_expr1 using inputs of variable
        values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        negate sub_expr1
        """
        return -sub_expr1.evaluation_at(val_dict)
    
    @staticmethod
    def derivative_at(sub_expr1, var, val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return -sub_expr1.derivative_at(var, val_dict, order)
    
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant 
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return -1

Ancestors (in MRO)

  • Neg
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression or constant var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression or constant 
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    return -1

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default set to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1, var, val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default set to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    return -sub_expr1.derivative_at(var, val_dict, order)

def evaluation_at(

sub_expr1, val_dict)

Compute negation of sub_expr1 using inputs of variable values from val_dict.

INPUTS

sub_expr1: expression or constant val_dict: a dictionary containing variable name and values.

RETURNS

negate sub_expr1

@staticmethod
def evaluation_at(sub_expr1, val_dict):
    """
    Compute negation of sub_expr1 using inputs of variable
    values from val_dict.

    INPUTS
    =======
    sub_expr1: expression or constant
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    negate sub_expr1
    """
    return -sub_expr1.evaluation_at(val_dict)

class Pow

This is a class to wrap up static method related to pow operation

class Pow:
    """ 
    This is a class to wrap up static method related to pow operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, sub_expr2, val_dict):
        """
        Compute sub_expr1 to the sub_expr2 power using inputs of variable
        values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sub_expr1 ** sub_expr2
        """
        return np.power(sub_expr1.evaluation_at(val_dict), 
                        sub_expr2.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1, sub_expr2, var, val_dict,order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        p = sub_expr2.evaluation_at(val_dict)
        if   order == 1:
            return p*np.power(sub_expr1.evaluation_at(val_dict), p-1.0) \
                   * sub_expr1.derivative_at(var, val_dict)
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                term1 = p*np.power(sub_expr1.evaluation_at(val_dict), p-1.0) \
                        * sub_expr1.derivative_at((var1, var2), val_dict, order=2)
                term2 = p*(p-1.0)*np.power(sub_expr1.evaluation_at(val_dict), p-2.0) \
                        * sub_expr1.derivative_at(var1, val_dict, order=1) \
                        * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2
            else:
                return Pow.derivative_at(sub_expr1, sub_expr2, (var, var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,sub_expr2,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        p = sub_expr2.val
        return p*np.power(sub_expr1.val, p-1.0)

Ancestors (in MRO)

  • Pow
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, sub_expr2, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression or constant sub_expr2: expression or constant var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,sub_expr2,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression or constant
    sub_expr2: expression or constant
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    p = sub_expr2.val
    return p*np.power(sub_expr1.val, p-1.0)

def derivative_at(

sub_expr1, sub_expr2, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression or constant sub_expr2: expression or constant val_dict: a dictionary containing variable name and values. var: variable of interest order: default set to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1, sub_expr2, var, val_dict,order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression or constant
    sub_expr2: expression or constant
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default set to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    p = sub_expr2.evaluation_at(val_dict)
    if   order == 1:
        return p*np.power(sub_expr1.evaluation_at(val_dict), p-1.0) \
               * sub_expr1.derivative_at(var, val_dict)
    elif order == 2:
        if type(var) is tuple:
            var1, var2 = var
            term1 = p*np.power(sub_expr1.evaluation_at(val_dict), p-1.0) \
                    * sub_expr1.derivative_at((var1, var2), val_dict, order=2)
            term2 = p*(p-1.0)*np.power(sub_expr1.evaluation_at(val_dict), p-2.0) \
                    * sub_expr1.derivative_at(var1, val_dict, order=1) \
                    * sub_expr1.derivative_at(var2, val_dict, order=1)
            return term1 + term2
        else:
            return Pow.derivative_at(sub_expr1, sub_expr2, (var, var), val_dict, order=2)
    else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')

def evaluation_at(

sub_expr1, sub_expr2, val_dict)

Compute sub_expr1 to the sub_expr2 power using inputs of variable values from val_dict.

INPUTS

sub_expr1: expression or constant sub_expr2: constant val_dict: a dictionary containing variable name and values.

RETURNS

sub_expr1 ** sub_expr2

@staticmethod
def evaluation_at(sub_expr1, sub_expr2, val_dict):
    """
    Compute sub_expr1 to the sub_expr2 power using inputs of variable
    values from val_dict.

    INPUTS
    =======
    sub_expr1: expression or constant
    sub_expr2: constant
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    sub_expr1 ** sub_expr2
    """
    return np.power(sub_expr1.evaluation_at(val_dict), 
                    sub_expr2.evaluation_at(val_dict))

class Sec

This is a class to wrap up static method related to sec operation

class Sec:
    """ 
    This is a class to wrap up static method related to sec operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute sec of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sec sub_expr1
        """
        return 1/np.cos(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * \
               np.tan(x) * (1/np.cos(x))
        else: raise NotImplementedError('higher order derivatives not implemented for sec.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x =sub_expr1.val
        return np.tan(x)/np.cos(x)

Ancestors (in MRO)

  • Sec
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x =sub_expr1.val
    return np.tan(x)/np.cos(x)

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1,var,val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    if order == 1:
        return sub_expr1.derivative_at(var, val_dict) * \
           np.tan(x) * (1/np.cos(x))
    else: raise NotImplementedError('higher order derivatives not implemented for sec.')

def evaluation_at(

sub_expr1, val_dict)

Compute sec of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

sec sub_expr1

@staticmethod
def evaluation_at(sub_expr1,val_dict):
    """
    Compute sec of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    sec sub_expr1
    """
    return 1/np.cos(sub_expr1.evaluation_at(val_dict))

class Sech

This is a class to wrap up static method related to sech operation

class Sech:
    """ 
    This is a class to wrap up static method related to sech operation
    """
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute sech of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sech sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return 1/np.cosh(x)
    
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        # d = -sech(x)tanh(x)
        d = -(1/np.cosh(x)) * (np.sinh(x)/np.cosh(x))
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict)*d
        else: raise NotImplementedError('higher order derivatives not implemented for sech.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return -(1/np.cosh(x)) * (np.sinh(x)/np.cosh(x))

Ancestors (in MRO)

  • Sech
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.val
    return -(1/np.cosh(x)) * (np.sinh(x)/np.cosh(x))

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

def derivative_at(sub_expr1,var,val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    # d = -sech(x)tanh(x)
    d = -(1/np.cosh(x)) * (np.sinh(x)/np.cosh(x))
    if order == 1:
        return sub_expr1.derivative_at(var, val_dict)*d
    else: raise NotImplementedError('higher order derivatives not implemented for sech.')

def evaluation_at(

sub_expr1, val_dict)

Compute sech of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

sech sub_expr1

def evaluation_at(sub_expr1,val_dict):
    """
    Compute sech of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    sech sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    return 1/np.cosh(x)

class Sin

This is a class to wrap up static method related to sin operation

class Sin:
    """ 
    This is a class to wrap up static method related to sin operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, val_dict):
        """
        Compute sin of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sin of sub_expr1 
        """
        return np.sin(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1, var, val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant 
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if   order == 1:
            return sub_expr1.derivative_at(var, val_dict) * \
        np.cos(sub_expr1.evaluation_at(val_dict))
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                f = sub_expr1.evaluation_at(val_dict)
                term1 =  np.cos(f) * sub_expr1.derivative_at(var,  val_dict, order=2)
                term2 = -np.sin(f) * sub_expr1.derivative_at(var1, val_dict, order=1) \
                                   * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2
            else:
                return Sin.derivative_at(sub_expr1, (var,var), val_dict, order=2)
        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return np.cos(sub_expr1.val)

Ancestors (in MRO)

  • Sin
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    return np.cos(sub_expr1.val)

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression or constant val_dict: a dictionary containing variable name and values. var: variable of interest order: default set to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1, var, val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression or constant 
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default set to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    if   order == 1:
        return sub_expr1.derivative_at(var, val_dict) * \
    np.cos(sub_expr1.evaluation_at(val_dict))
    elif order == 2:
        if type(var) is tuple:
            var1, var2 = var
            f = sub_expr1.evaluation_at(val_dict)
            term1 =  np.cos(f) * sub_expr1.derivative_at(var,  val_dict, order=2)
            term2 = -np.sin(f) * sub_expr1.derivative_at(var1, val_dict, order=1) \
                               * sub_expr1.derivative_at(var2, val_dict, order=1)
            return term1 + term2
        else:
            return Sin.derivative_at(sub_expr1, (var,var), val_dict, order=2)
    else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')

def evaluation_at(

sub_expr1, val_dict)

Compute sin of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

sin of sub_expr1

@staticmethod
def evaluation_at(sub_expr1, val_dict):
    """
    Compute sin of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    sin of sub_expr1 
    """
    return np.sin(sub_expr1.evaluation_at(val_dict))

class Sinh

This is a class to wrap up static method related to sinh operation

class Sinh:
    """ 
    This is a class to wrap up static method related to sinh operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute sinh of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sinh sub_expr1
        """
        return np.sinh(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * np.cosh(x)
        else: raise NotImplementedError('higher order derivatives not implemented for sinh.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        return np.cosh(x)

Ancestors (in MRO)

  • Sinh
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.val
    return np.cosh(x)

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1,var,val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    if order == 1:
        return sub_expr1.derivative_at(var, val_dict) * np.cosh(x)
    else: raise NotImplementedError('higher order derivatives not implemented for sinh.')

def evaluation_at(

sub_expr1, val_dict)

Compute sinh of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

sinh sub_expr1

@staticmethod
def evaluation_at(sub_expr1,val_dict):
    """
    Compute sinh of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    sinh sub_expr1
    """
    return np.sinh(sub_expr1.evaluation_at(val_dict))

class Sub

This is a class to wrap up static method related to sub operation

class Sub:
    """ 
    This is a class to wrap up static method related to sub operation
    """
    @staticmethod
    def evaluation_at(sub_expr1, sub_expr2, val_dict):
        """
        Compute subtraction of sub_expr2 from sub_expr1 using inputs of variable
        values from val_dict.
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        sub_expr1 - sub_expr2
        """
        return sub_expr1.evaluation_at(val_dict) - \
               sub_expr2.evaluation_at(val_dict)
    @staticmethod
    def derivative_at(sub_expr1, sub_expr2, var, val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant
        sub_expr2: expression or constant
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default set to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return sub_expr1.derivative_at(var, val_dict, order) - \
               sub_expr2.derivative_at(var, val_dict, order)
    @staticmethod
    def backderivative_at(sub_expr1,sub_expr2,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression or constant 
        sub_expr2: expression or constant 
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if var == sub_expr1:
            return 1
        if var == sub_expr2:
            return -1 

Ancestors (in MRO)

  • Sub
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, sub_expr2, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression or constant sub_expr2: expression or constant var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,sub_expr2,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression or constant 
    sub_expr2: expression or constant 
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    if var == sub_expr1:
        return 1
    if var == sub_expr2:
        return -1 

def derivative_at(

sub_expr1, sub_expr2, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression or constant sub_expr2: expression or constant val_dict: a dictionary containing variable name and values. var: variable of interest order: default set to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1, sub_expr2, var, val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression or constant
    sub_expr2: expression or constant
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default set to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    return sub_expr1.derivative_at(var, val_dict, order) - \
           sub_expr2.derivative_at(var, val_dict, order)

def evaluation_at(

sub_expr1, sub_expr2, val_dict)

Compute subtraction of sub_expr2 from sub_expr1 using inputs of variable values from val_dict.

INPUTS

sub_expr1: expression or constant sub_expr2: expression or constant val_dict: a dictionary containing variable name and values.

RETURNS

sub_expr1 - sub_expr2

@staticmethod
def evaluation_at(sub_expr1, sub_expr2, val_dict):
    """
    Compute subtraction of sub_expr2 from sub_expr1 using inputs of variable
    values from val_dict.

    INPUTS
    =======
    sub_expr1: expression or constant
    sub_expr2: expression or constant
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    sub_expr1 - sub_expr2
    """
    return sub_expr1.evaluation_at(val_dict) - \
           sub_expr2.evaluation_at(val_dict)

class Tan

This is a class to wrap up static method related to tan operation

class Tan:
    """ 
    This is a class to wrap up static method related to tan operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute tan of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        tan sub_expr1
        """
        return np.tan(sub_expr1.evaluation_at(val_dict))
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression or constant
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        if   order == 1:
            return sub_expr1.derivative_at(var, val_dict) /(np.cos(sub_expr1.evaluation_at(val_dict))**2)
        elif order == 2:
            if type(var) is tuple:
                var1, var2 = var
                f = sub_expr1.evaluation_at(val_dict)
                term1 = 1/(np.cos(f)**2) * sub_expr1.derivative_at(var,  val_dict, order=2)
                term2 = 2*np.tan(f)/(np.cos(f)**2) \
                        * sub_expr1.derivative_at(var1, val_dict, order=1) \
                        * sub_expr1.derivative_at(var2, val_dict, order=1)
                return term1 + term2
            else:
                return Tan.derivative_at(sub_expr1, (var,var), val_dict, order=2)

        else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        return 1/(np.cos(sub_expr1.val)**2)

Ancestors (in MRO)

  • Tan
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    return 1/(np.cos(sub_expr1.val)**2)

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression or constant val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1,var,val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression or constant
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    if   order == 1:
        return sub_expr1.derivative_at(var, val_dict) /(np.cos(sub_expr1.evaluation_at(val_dict))**2)
    elif order == 2:
        if type(var) is tuple:
            var1, var2 = var
            f = sub_expr1.evaluation_at(val_dict)
            term1 = 1/(np.cos(f)**2) * sub_expr1.derivative_at(var,  val_dict, order=2)
            term2 = 2*np.tan(f)/(np.cos(f)**2) \
                    * sub_expr1.derivative_at(var1, val_dict, order=1) \
                    * sub_expr1.derivative_at(var2, val_dict, order=1)
            return term1 + term2
        else:
            return Tan.derivative_at(sub_expr1, (var,var), val_dict, order=2)
    else: raise NotImplementedError('3rd order or higher derivatives are not implemented.')

def evaluation_at(

sub_expr1, val_dict)

Compute tan of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

tan sub_expr1

@staticmethod
def evaluation_at(sub_expr1,val_dict):
    """
    Compute tan of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    tan sub_expr1
    """
    return np.tan(sub_expr1.evaluation_at(val_dict))

class Tanh

This is a class to wrap up static method related to tanh operation

class Tanh:
    """ 
    This is a class to wrap up static method related to tanh operation
    """
    @staticmethod
    def evaluation_at(sub_expr1,val_dict):
        """
        Compute tanh of sub_expr1 with inputs of variable values from val_dict.
    
        INPUTS
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        tanh sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        return np.sinh(x)/np.cosh(x)
    
    @staticmethod
    def derivative_at(sub_expr1,var,val_dict, order=1):
        """
        calculate 1st derivative of var using forward mode
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        val_dict: a dictionary containing variable name and values.
        var: variable of interest
        order: default to 1, set to 2 if 2nd derivative is desired
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.evaluation_at(val_dict)
        tanh = np.sinh(x)/np.cosh(x)
        if order == 1:
            return sub_expr1.derivative_at(var, val_dict) * (1-tanh*tanh)
        else: raise NotImplementedError('higher order derivatives not implemented for tanh.')
    @staticmethod
    def backderivative_at(sub_expr1,var):
        """
        calculate 1st derivative of var using back propagation
    
        INPUTS
        =======
        sub_expr1: expression whose components include var(or itself be to var)
        var: variable of interest
        
        RETURNS
        ========
        derivative of var with respect to sub_expr1
        """
        x = sub_expr1.val
        tanh = np.sinh(x)/np.cosh(x)
        return 1-tanh*tanh

Ancestors (in MRO)

  • Tanh
  • builtins.object

Static methods

def backderivative_at(

sub_expr1, var)

calculate 1st derivative of var using back propagation

INPUTS

sub_expr1: expression whose components include var(or itself be to var) var: variable of interest

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def backderivative_at(sub_expr1,var):
    """
    calculate 1st derivative of var using back propagation

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    var: variable of interest
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.val
    tanh = np.sinh(x)/np.cosh(x)
    return 1-tanh*tanh

def derivative_at(

sub_expr1, var, val_dict, order=1)

calculate 1st derivative of var using forward mode

INPUTS

sub_expr1: expression whose components include var(or itself be to var) val_dict: a dictionary containing variable name and values. var: variable of interest order: default to 1, set to 2 if 2nd derivative is desired

RETURNS

derivative of var with respect to sub_expr1

@staticmethod
def derivative_at(sub_expr1,var,val_dict, order=1):
    """
    calculate 1st derivative of var using forward mode

    INPUTS
    =======
    sub_expr1: expression whose components include var(or itself be to var)
    val_dict: a dictionary containing variable name and values.
    var: variable of interest
    order: default to 1, set to 2 if 2nd derivative is desired
    
    RETURNS
    ========
    derivative of var with respect to sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    tanh = np.sinh(x)/np.cosh(x)
    if order == 1:
        return sub_expr1.derivative_at(var, val_dict) * (1-tanh*tanh)
    else: raise NotImplementedError('higher order derivatives not implemented for tanh.')

def evaluation_at(

sub_expr1, val_dict)

Compute tanh of sub_expr1 with inputs of variable values from val_dict.

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

tanh sub_expr1

@staticmethod
def evaluation_at(sub_expr1,val_dict):
    """
    Compute tanh of sub_expr1 with inputs of variable values from val_dict.

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    tanh sub_expr1
    """
    x = sub_expr1.evaluation_at(val_dict)
    return np.sinh(x)/np.cosh(x)

class Variable

This is a class for representing variable.

class Variable(Expression):
    """ 
    This is a class for representing variable. 
    """
    def __init__(self):
        """ 
        The constructor for VectorFunction class. 
        It has no parameters: 
        """
        self.val = None
        self.bder = 0
        return
    
    def evaluation_at(self, val_dict):
        """ 
        The function to evaluation the value of variable class
        
        PARAMETERS:
        =======
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ======== 
        a scalar value 
        """
        return val_dict[self]
    
    def derivative_at(self, var, val_dict, order=1):
        """ 
        The function calculates derivative of variable class. 
  
        PARAMETERS:
        =======
        val_dict: a dictionary containing variable name and values.
        var: variable whose derivative is the result of this function
        order: default set to 1 for 1st derivative, change to 2 for 
        higher order
        
        RETURNS
        ========
        scalar value  
        """
        if order == 1:
            return 1.0 if var is self else 0.0
        else:
            return 0.0
    
    def __eq__(self, another):
        """ Implement dunder method for equal """
        return another is self
    
    def __ne__(self, another):
        """ Implement dunder method for not equal """
        return ~self.__eq__(another)
    
    def __hash__(self):
        """ Implement dunder method for hash """
        return Expression.__hash__(self) 

Ancestors (in MRO)

Static methods

def __init__(

self)

The constructor for VectorFunction class. It has no parameters:

def __init__(self):
    """ 
    The constructor for VectorFunction class. 
    It has no parameters: 
    """
    self.val = None
    self.bder = 0
    return

def back_derivative(

self, var, val_dict)

The wrapper function for individual backderivative_at function of self_ele_func

PARAMETERS:

val_dict: a dictionary containing variable name and values. Variables in val_dict are of atomic feature and cannot be further decomposed. var: variable with respect to which the function calculates derivative

RETURNS

derivative of var with respect to the immediate parent that contain var

def back_derivative(self,var,val_dict):
    """
    The wrapper function for individual backderivative_at 
    function of self_ele_func
    
    PARAMETERS:
    =======
    val_dict: a dictionary containing variable name and values. Variables
    in val_dict are of atomic feature and cannot be further decomposed.
    var: variable with respect to which the function calculates derivative   
    
    RETURNS
    ========
    derivative of var with respect to the immediate parent that contain var
    """
    if var is self: return 1.0
    if self._sub_expr2 is None:
        return self._ele_func.backderivative_at(self._sub_expr1,var)
    else:
        return self._ele_func.backderivative_at(self._sub_expr1,
                                                self._sub_expr2,var)    

def derivative_at(

self, var, val_dict, order=1)

The function calculates derivative of variable class.

PARAMETERS:

val_dict: a dictionary containing variable name and values. var: variable whose derivative is the result of this function order: default set to 1 for 1st derivative, change to 2 for higher order

RETURNS

scalar value

def derivative_at(self, var, val_dict, order=1):
    """ 
    The function calculates derivative of variable class. 
    PARAMETERS:
    =======
    val_dict: a dictionary containing variable name and values.
    var: variable whose derivative is the result of this function
    order: default set to 1 for 1st derivative, change to 2 for 
    higher order
    
    RETURNS
    ========
    scalar value  
    """
    if order == 1:
        return 1.0 if var is self else 0.0
    else:
        return 0.0

def evaluation_at(

self, val_dict)

The function to evaluation the value of variable class

PARAMETERS:

val_dict: a dictionary containing variable name and values.

RETURNS

a scalar value

def evaluation_at(self, val_dict):
    """ 
    The function to evaluation the value of variable class
    
    PARAMETERS:
    =======
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ======== 
    a scalar value 
    """
    return val_dict[self]

def gradient_at(

self, val_dict, returns_dict=False)

calculate 1st derivative of variables in val_dict using forward mode

INPUTS

val_dict: a dictionary containing variable name and values. returns_dict: the format of output

RETURNS

derivative of variables in val_dict with respect to the current expression, stored in a dictionary or a 2-D numpy array

def gradient_at(self, val_dict, returns_dict=False):
    """
    calculate 1st derivative of variables in val_dict using forward mode

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
    returns_dict: the format of output
     
    RETURNS
    ========
    derivative of variables in val_dict with respect to the current 
    expression, stored in a dictionary or a 2-D numpy array
    """
    if returns_dict:
        return {v: self.derivative_at(v, val_dict) for v in val_dict.keys()}
    return np.array([self.derivative_at(var, val_dict, order=1) 
                     for var in val_dict.keys()])

def hessian_at(

self, val_dict)

calculate 2nd derivative of variables in val_dict using forward mode

INPUTS

val_dict: a dictionary containing variable name and values.

RETURNS

2nd derivative of variables in val_dict with respect to the current expression, stored in a 2-D list

def hessian_at(self, val_dict):
    """
    calculate 2nd derivative of variables in val_dict using forward mode

    INPUTS
    =======
    val_dict: a dictionary containing variable name and values.
     
    RETURNS
    ========
    2nd derivative of variables in val_dict with respect to the current 
    expression, stored in a 2-D list
    """
    return np.array( [ \
                      [self.derivative_at((var1, var2), val_dict, order=2)
                       for var1 in val_dict.keys()]
                      for var2 in val_dict.keys() \
                      ] )

Instance variables

var bder

var val

Inheritance: Expression.val

class VectorFunction

This is a class for applying operations to a vector of variables.

Attributes: _exprlist: a list of expressions with respect to which the operations are applied

class VectorFunction:
    """ 
    This is a class for applying operations to a vector of variables. 
      
    Attributes: 
        _exprlist: a list of expressions with respect to which the operations
    are applied 
    """
    def __init__(self, exprlist):
        """ 
        The constructor for VectorFunction class. 
        
        PARAMETERS:
        ======= 
        exprlist: a list of expressions with respect to which the class 
        functions are applied to  
        """
        self._exprlist = exprlist.copy()
    
    def evaluation_at(self, val_dict):
        """ 
        The function to apply evaluation_at to a vector of expressions. 
  
        PARAMETERS:
        ======= 
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        a numpy array containing value of expressions in the self._exprlist. 
        """
        return np.array([expr.evaluation_at(val_dict) 
                        for expr in self._exprlist])
    
    def gradient_at(self, var, val_dict):
        """ 
        The function to apply derivative_at to a vector of expressions. 
  
        PARAMETERS:
        =======
        val_dict: a dictionary containing variable name and values.
        var: variable whose derivative is the result of this function
       
        RETURNS
        ========
        a numpy array containing first derivative of expressions in 
        self._exprlist with respect to var. 
        """
        return np.array([f.derivative_at(var, val_dict) for f in self._exprlist])
    
    def jacobian_at(self, val_dict):
        """ 
        The function to calculate jacobian with respect to atomic variables in 
        val_dict. 
  
        PARAMETERS:
        ======= 
        val_dict: a dictionary containing variable name and values.
        
        RETURNS
        ========
        a 2-D numpy array containing derivatives of variables in val_dict 
        with resepct to expressions in self._exprlist. 
        """
        return np.array([self.gradient_at(var, val_dict)
                         for var in val_dict.keys()]).transpose()

Ancestors (in MRO)

Static methods

def __init__(

self, exprlist)

The constructor for VectorFunction class.

PARAMETERS:

exprlist: a list of expressions with respect to which the class functions are applied to

def __init__(self, exprlist):
    """ 
    The constructor for VectorFunction class. 
    
    PARAMETERS:
    ======= 
    exprlist: a list of expressions with respect to which the class 
    functions are applied to  
    """
    self._exprlist = exprlist.copy()

def evaluation_at(

self, val_dict)

The function to apply evaluation_at to a vector of expressions.

PARAMETERS:

val_dict: a dictionary containing variable name and values.

RETURNS

a numpy array containing value of expressions in the self._exprlist.

def evaluation_at(self, val_dict):
    """ 
    The function to apply evaluation_at to a vector of expressions. 
    PARAMETERS:
    ======= 
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    a numpy array containing value of expressions in the self._exprlist. 
    """
    return np.array([expr.evaluation_at(val_dict) 
                    for expr in self._exprlist])

def gradient_at(

self, var, val_dict)

The function to apply derivative_at to a vector of expressions.

PARAMETERS:

val_dict: a dictionary containing variable name and values. var: variable whose derivative is the result of this function

RETURNS

a numpy array containing first derivative of expressions in self._exprlist with respect to var.

def gradient_at(self, var, val_dict):
    """ 
    The function to apply derivative_at to a vector of expressions. 
    PARAMETERS:
    =======
    val_dict: a dictionary containing variable name and values.
    var: variable whose derivative is the result of this function
   
    RETURNS
    ========
    a numpy array containing first derivative of expressions in 
    self._exprlist with respect to var. 
    """
    return np.array([f.derivative_at(var, val_dict) for f in self._exprlist])

def jacobian_at(

self, val_dict)

The function to calculate jacobian with respect to atomic variables in val_dict.

PARAMETERS:

val_dict: a dictionary containing variable name and values.

RETURNS

a 2-D numpy array containing derivatives of variables in val_dict with resepct to expressions in self._exprlist.

def jacobian_at(self, val_dict):
    """ 
    The function to calculate jacobian with respect to atomic variables in 
    val_dict. 
    PARAMETERS:
    ======= 
    val_dict: a dictionary containing variable name and values.
    
    RETURNS
    ========
    a 2-D numpy array containing derivatives of variables in val_dict 
    with resepct to expressions in self._exprlist. 
    """
    return np.array([self.gradient_at(var, val_dict)
                     for var in val_dict.keys()]).transpose()

Autodiff.backprop

backprop API documentation Top

backprop module

This file contains the back propagation feature using interface designed in forward.py

"""
This file contains the back propagation feature using interface designed in 
forward.py
"""
import autodiff.forward as fwd

def forward_pass(y,val_dict):
    """ 
    The function evaluating each variable/constant and storing their values
    in .val attributes, using atomic variable values from val_dict, in a 
    recursive fashion, starting from root node in the computational graph
        
    INPUTS:
    =======
    val_dict: a dictionary containing variable name and values.
    y: the highest node(root) encompassing all variable in the computational 
    graph
    """
    # forward pass, store values
    if type(y) == fwd.Expression:
        y.val = y.evaluation_at(val_dict)
        if y._sub_expr1 != None:
            forward_pass(y._sub_expr1,val_dict)
        if y._sub_expr2!=None:
            forward_pass(y._sub_expr2,val_dict)
    elif isinstance(y,fwd.Variable):
        y.val = val_dict[y]
    return 

def initialize(top,y):
    """ 
    The function initializing derivative values of each variable/constant
    in the computational graph with respect to the root.
        
    INPUTS:
    =======
    y: the highest node(root) encompassing all variable in the computational 
    graph
    """
    #print(y.val)
    if y == top:
        y.bder = 1 
    else:
        y.bder = 0
    if not isinstance(y,fwd.Variable) and isinstance(y._sub_expr1,fwd.Expression) and not isinstance(y._sub_expr1, fwd.Constant):
        initialize(top,y._sub_expr1)
    if not isinstance(y,fwd.Variable) and isinstance(y._sub_expr2,fwd.Expression) and not isinstance(y._sub_expr2, fwd.Constant):
        initialize(top,y._sub_expr2)   
    return

def backward(y,val_dict,depth = 0):
    """ 
    The function calculating derivative values of each variable/constant
    in the computational graph with respect to the root in a recursive fashion,
    starting from the root node.
        
    INPUTS:
    =======
    y: the highest node(root) encompassing all variable in the computational 
    graph
    val_dict: a dictionary containing variable name and values.
    """
    # val_dict stores the basic variables
    # (atomic,cannot be further decomposed)
    if type(y) == fwd.Expression:
        if y._sub_expr1 != None and isinstance(y._sub_expr1,fwd.Expression):
            y._sub_expr1.bder += y.bder*y.back_derivative(y._sub_expr1,val_dict)
            backward(y._sub_expr1,val_dict,depth+1)
        if y._sub_expr2 != None and isinstance(y._sub_expr2,fwd.Expression) and not isinstance(y._sub_expr2, fwd.Constant):
            y._sub_expr2.bder += y.bder*y.back_derivative(y._sub_expr2,val_dict)
            backward(y._sub_expr2,val_dict,depth+1)
    return 

def back_propagation(y,val_dict):
    """ 
    The wrapper function for three steps of back propogation.
    After calling this function, the .bder attributes of each variables/constant
    in the computational graph stores its first derivative with respect to
    the root node.
        
    INPUTS:
    =======
    y: the highest node(root) encompassing all variable in the computational 
    graph
    val_dict: a dictionary containing variable name and values.
    """
    # get backprop derivative with respect to y at every node lower than y
    forward_pass(y,val_dict)
    initialize(y,y)
    backward(y,val_dict)

Functions

def back_propagation(

y, val_dict)

The wrapper function for three steps of back propogation. After calling this function, the .bder attributes of each variables/constant in the computational graph stores its first derivative with respect to the root node.

INPUTS:

y: the highest node(root) encompassing all variable in the computational graph val_dict: a dictionary containing variable name and values.

def back_propagation(y,val_dict):
    """ 
    The wrapper function for three steps of back propogation.
    After calling this function, the .bder attributes of each variables/constant
    in the computational graph stores its first derivative with respect to
    the root node.
        
    INPUTS:
    =======
    y: the highest node(root) encompassing all variable in the computational 
    graph
    val_dict: a dictionary containing variable name and values.
    """
    # get backprop derivative with respect to y at every node lower than y
    forward_pass(y,val_dict)
    initialize(y,y)
    backward(y,val_dict)

def backward(

y, val_dict, depth=0)

The function calculating derivative values of each variable/constant in the computational graph with respect to the root in a recursive fashion, starting from the root node.

INPUTS:

y: the highest node(root) encompassing all variable in the computational graph val_dict: a dictionary containing variable name and values.

def backward(y,val_dict,depth = 0):
    """ 
    The function calculating derivative values of each variable/constant
    in the computational graph with respect to the root in a recursive fashion,
    starting from the root node.
        
    INPUTS:
    =======
    y: the highest node(root) encompassing all variable in the computational 
    graph
    val_dict: a dictionary containing variable name and values.
    """
    # val_dict stores the basic variables
    # (atomic,cannot be further decomposed)
    if type(y) == fwd.Expression:
        if y._sub_expr1 != None and isinstance(y._sub_expr1,fwd.Expression):
            y._sub_expr1.bder += y.bder*y.back_derivative(y._sub_expr1,val_dict)
            backward(y._sub_expr1,val_dict,depth+1)
        if y._sub_expr2 != None and isinstance(y._sub_expr2,fwd.Expression) and not isinstance(y._sub_expr2, fwd.Constant):
            y._sub_expr2.bder += y.bder*y.back_derivative(y._sub_expr2,val_dict)
            backward(y._sub_expr2,val_dict,depth+1)
    return 

def forward_pass(

y, val_dict)

The function evaluating each variable/constant and storing their values in .val attributes, using atomic variable values from val_dict, in a recursive fashion, starting from root node in the computational graph

INPUTS:

val_dict: a dictionary containing variable name and values. y: the highest node(root) encompassing all variable in the computational graph

def forward_pass(y,val_dict):
    """ 
    The function evaluating each variable/constant and storing their values
    in .val attributes, using atomic variable values from val_dict, in a 
    recursive fashion, starting from root node in the computational graph
        
    INPUTS:
    =======
    val_dict: a dictionary containing variable name and values.
    y: the highest node(root) encompassing all variable in the computational 
    graph
    """
    # forward pass, store values
    if type(y) == fwd.Expression:
        y.val = y.evaluation_at(val_dict)
        if y._sub_expr1 != None:
            forward_pass(y._sub_expr1,val_dict)
        if y._sub_expr2!=None:
            forward_pass(y._sub_expr2,val_dict)
    elif isinstance(y,fwd.Variable):
        y.val = val_dict[y]
    return 

def initialize(

top, y)

The function initializing derivative values of each variable/constant in the computational graph with respect to the root.

INPUTS:

y: the highest node(root) encompassing all variable in the computational graph

def initialize(top,y):
    """ 
    The function initializing derivative values of each variable/constant
    in the computational graph with respect to the root.
        
    INPUTS:
    =======
    y: the highest node(root) encompassing all variable in the computational 
    graph
    """
    #print(y.val)
    if y == top:
        y.bder = 1 
    else:
        y.bder = 0
    if not isinstance(y,fwd.Variable) and isinstance(y._sub_expr1,fwd.Expression) and not isinstance(y._sub_expr1, fwd.Constant):
        initialize(top,y._sub_expr1)
    if not isinstance(y,fwd.Variable) and isinstance(y._sub_expr2,fwd.Expression) and not isinstance(y._sub_expr2, fwd.Constant):
        initialize(top,y._sub_expr2)   
    return

Autodiff.rootfinding

rootfinding API documentation Top

rootfinding module

This file contains some root finding algorithms built on top of autodiff.

"""
This file contains some root finding algorithms built on top of autodiff.
"""



from autodiff.backprop import back_propagation

def newton_scalar(f, init_val_dict, max_itr, method = 'forward',tol=1e-8):
    """
    Newton's Method finding 0 root for a single expression 
    
    INPUTS
    =======
    f: expression 
    init_val_dict: dictionary containing initial value of variables
    max_itr: maximum iteration before the algorithm stops
    method: string, default set to 'forward mode', in all other cases 
    use 'backward'
    tol: tolerance, the minimum threshold for absolute 
    difference of value of f from 0 for the algorithm to stop
    
    RETURNS
    ========
    variable values corresponding to the 0 point of f
    """
    itr = 1
    val_dict = init_val_dict.copy()
    
    while True:
        evalf = f.evaluation_at(val_dict)
        if method == 'forward':    
            derif = {v: f.derivative_at(v, val_dict) for v in val_dict.keys()}
        else:
            back_propagation(f,val_dict)
            derif = {v:v.bder for v in val_dict.keys()}
        
        for v in val_dict.keys():
            val_dict[v] = val_dict[v] - evalf/derif[v]

        if abs(f.evaluation_at(val_dict)) <= tol: break

        if itr > max_itr:
            print("Exceeded allowable max iterations without finding a root.")

            break
        
        itr += 1
        
    return val_dict

Functions

def newton_scalar(

f, init_val_dict, max_itr, method='forward', tol=1e-08)

Newton's Method finding 0 root for a single expression

INPUTS

f: expression init_val_dict: dictionary containing initial value of variables max_itr: maximum iteration before the algorithm stops method: string, default set to 'forward mode', in all other cases use 'backward' tol: tolerance, the minimum threshold for absolute difference of value of f from 0 for the algorithm to stop

RETURNS

variable values corresponding to the 0 point of f

def newton_scalar(f, init_val_dict, max_itr, method = 'forward',tol=1e-8):
    """
    Newton's Method finding 0 root for a single expression 
    
    INPUTS
    =======
    f: expression 
    init_val_dict: dictionary containing initial value of variables
    max_itr: maximum iteration before the algorithm stops
    method: string, default set to 'forward mode', in all other cases 
    use 'backward'
    tol: tolerance, the minimum threshold for absolute 
    difference of value of f from 0 for the algorithm to stop
    
    RETURNS
    ========
    variable values corresponding to the 0 point of f
    """
    itr = 1
    val_dict = init_val_dict.copy()
    
    while True:
        evalf = f.evaluation_at(val_dict)
        if method == 'forward':    
            derif = {v: f.derivative_at(v, val_dict) for v in val_dict.keys()}
        else:
            back_propagation(f,val_dict)
            derif = {v:v.bder for v in val_dict.keys()}
        
        for v in val_dict.keys():
            val_dict[v] = val_dict[v] - evalf/derif[v]

        if abs(f.evaluation_at(val_dict)) <= tol: break

        if itr > max_itr:
            print("Exceeded allowable max iterations without finding a root.")

            break
        
        itr += 1
        
    return val_dict

Autodiff.optimize

optimize API documentation Top

optimize module

import numpy as np
from numpy.linalg import multi_dot
from numpy.linalg import norm
from scipy.linalg import solve

def bfgs(f, init_val_dict, max_iter=2000, stop_stepsize=1e-8):
    """
    Broyden–Fletcher–Goldfarb–Shanno finding minimum for a 
    single expression
    
    INPUTS
    =======
    f: expression 
    init_val_dict:dictionary containing initial value of variables
    max_iter: maximum iteration before the algorithm stops
    stop_stepsize: tolerance, the minimum threshold for absolute 
    difference of value of f from 0 for the algorithm to stop
    
    RETURNS
    ========
    variable values corresponding to the minimum value of f
    """
    variables  = [var for var in init_val_dict.keys()]
    curr_point = np.array([v for k, v in init_val_dict.items()])
    B          = np.eye(len(curr_point))
    
    for i in range(max_iter):
        
        # solve Bs = - (gradient of f at x)
        curr_val_dict = {var: val for var, val in zip(variables, curr_point)}
        f_grad = f.gradient_at(curr_val_dict)
        s = solve(B, -f_grad)
        if norm(s, ord=2) < stop_stepsize: break
            
        # x_next := x + s
        next_point = curr_point + s

        # y := (gradient of f at x_next) - (gradient of f at x)
        # x := x_next
        next_val_dict = {var: val for var, val in zip(variables, next_point)}
        y = f.gradient_at(next_val_dict) - f.gradient_at(curr_val_dict)
        curr_point = next_point
        
        # B := B + deltaB
        s, y = s.reshape(-1, 1), y.reshape(-1, 1)
        deltaB = multi_dot([y, y.T])/multi_dot([y.T, s]) \
                 - multi_dot([B, s, s.T, B])/multi_dot([s.T, B, s]) 
        B = B + deltaB
    
    return {var: val for var, val in zip(variables, curr_point)}


def newton(f,  init_val_dict, max_iter=1000, stop_stepsize=1e-8,return_history=False):
    """
    Newton's Method finding minimum for a single expression
    
    INPUTS
    =======
    f: expression 
    init_val_dict:dictionary containing initial value of variables
    max_itr: maximum iteration before the algorithm stops
    stop_stepsize: tolerance, the minimum threshold for absolute 
    difference of value of f from 0 for the algorithm to stop
    return_history: default set to False. If True, return the trajectory
    of the algorithm including the final answer
    
    RETURNS
    ========
    If return_history = False: variable values corresponding to the 
    minimum value of f
    If return_history = True, return the trajectory
    of the algorithm including the final answer
    """
    variables  = [var for var in init_val_dict.keys()]
    curr_point = np.array([v for k, v in init_val_dict.items()])
    f_grad = f.gradient_at(init_val_dict)
    f_hess = f.hessian_at(init_val_dict)
    history = [curr_point.tolist()]
    
    for i in range(max_iter):
        
        curr_val_dict = {var: val for var, val in zip(variables, curr_point)}
        # solve (Hessian of f at x)s = - (gradient of f at x)
        f_grad =f.gradient_at(curr_val_dict)
        f_hess = f.hessian_at(curr_val_dict)

        step = np.linalg.solve(f_hess, -f_grad)
        if np.linalg.norm(step, ord=2) < stop_stepsize: break
        
        # x := x + s
        curr_point = curr_point + step
        history.append(curr_point.tolist())
    
    if return_history:
        return history

    return {var: val for var, val in zip(variables, curr_point)}



def gradient_descent(f,init_val_dict, learning_rate=0.001, max_iter=1000, stop_stepsize=1e-6,return_history=False):
    """
    Gradient Descent finding minimum for a 
    single expression
    
    INPUTS
    =======
    f: expression 
    init_val_dict:dictionary containing initial value of variables
    learning_rate: the step size between iterations
    max_iter: maximum iteration before the algorithm stops
    stop_stepsize: tolerance, the minimum threshold for absolute 
    difference of value of f from 0 for the algorithm to stop
    return_history: default set to False. If True, return the trajectory
    of the algorithm including the final answer
    
    RETURNS
    ========
    If return_history = False: variable values corresponding to the 
    minimum value of f
    If return_history = True, return the trajectory
    of the algorithm including the final answer
    """
    f_grad = f.gradient_at(init_val_dict)
    variables  = [var for var in init_val_dict.keys()]
    curr_point = np.array([v for k, v in init_val_dict.items()])
    history = [curr_point.tolist()]
    
    for i in range(max_iter):
        
        prev_point =curr_point
        prev_val_dict = {var: val for var, val in zip(variables, prev_point)}
        f_grad =f.gradient_at(prev_val_dict)

        curr_point =curr_point - learning_rate*f_grad
        history.append(curr_point.tolist())
        if np.linalg.norm(curr_point-prev_point, ord=2) < stop_stepsize: break
        
    if return_history:
        return history

    return {var: val for var, val in zip(variables, curr_point)}

Functions

def bfgs(

f, init_val_dict, max_iter=2000, stop_stepsize=1e-08)

Broyden–Fletcher–Goldfarb–Shanno finding minimum for a single expression

INPUTS

f: expression init_val_dict:dictionary containing initial value of variables max_iter: maximum iteration before the algorithm stops stop_stepsize: tolerance, the minimum threshold for absolute difference of value of f from 0 for the algorithm to stop

RETURNS

variable values corresponding to the minimum value of f

def bfgs(f, init_val_dict, max_iter=2000, stop_stepsize=1e-8):
    """
    Broyden–Fletcher–Goldfarb–Shanno finding minimum for a 
    single expression
    
    INPUTS
    =======
    f: expression 
    init_val_dict:dictionary containing initial value of variables
    max_iter: maximum iteration before the algorithm stops
    stop_stepsize: tolerance, the minimum threshold for absolute 
    difference of value of f from 0 for the algorithm to stop
    
    RETURNS
    ========
    variable values corresponding to the minimum value of f
    """
    variables  = [var for var in init_val_dict.keys()]
    curr_point = np.array([v for k, v in init_val_dict.items()])
    B          = np.eye(len(curr_point))
    
    for i in range(max_iter):
        
        # solve Bs = - (gradient of f at x)
        curr_val_dict = {var: val for var, val in zip(variables, curr_point)}
        f_grad = f.gradient_at(curr_val_dict)
        s = solve(B, -f_grad)
        if norm(s, ord=2) < stop_stepsize: break
            
        # x_next := x + s
        next_point = curr_point + s

        # y := (gradient of f at x_next) - (gradient of f at x)
        # x := x_next
        next_val_dict = {var: val for var, val in zip(variables, next_point)}
        y = f.gradient_at(next_val_dict) - f.gradient_at(curr_val_dict)
        curr_point = next_point
        
        # B := B + deltaB
        s, y = s.reshape(-1, 1), y.reshape(-1, 1)
        deltaB = multi_dot([y, y.T])/multi_dot([y.T, s]) \
                 - multi_dot([B, s, s.T, B])/multi_dot([s.T, B, s]) 
        B = B + deltaB
    
    return {var: val for var, val in zip(variables, curr_point)}

def gradient_descent(

f, init_val_dict, learning_rate=0.001, max_iter=1000, stop_stepsize=1e-06, return_history=False)

Gradient Descent finding minimum for a single expression

INPUTS

f: expression init_val_dict:dictionary containing initial value of variables learning_rate: the step size between iterations max_iter: maximum iteration before the algorithm stops stop_stepsize: tolerance, the minimum threshold for absolute difference of value of f from 0 for the algorithm to stop return_history: default set to False. If True, return the trajectory of the algorithm including the final answer

RETURNS

If return_history = False: variable values corresponding to the minimum value of f If return_history = True, return the trajectory of the algorithm including the final answer

def gradient_descent(f,init_val_dict, learning_rate=0.001, max_iter=1000, stop_stepsize=1e-6,return_history=False):
    """
    Gradient Descent finding minimum for a 
    single expression
    
    INPUTS
    =======
    f: expression 
    init_val_dict:dictionary containing initial value of variables
    learning_rate: the step size between iterations
    max_iter: maximum iteration before the algorithm stops
    stop_stepsize: tolerance, the minimum threshold for absolute 
    difference of value of f from 0 for the algorithm to stop
    return_history: default set to False. If True, return the trajectory
    of the algorithm including the final answer
    
    RETURNS
    ========
    If return_history = False: variable values corresponding to the 
    minimum value of f
    If return_history = True, return the trajectory
    of the algorithm including the final answer
    """
    f_grad = f.gradient_at(init_val_dict)
    variables  = [var for var in init_val_dict.keys()]
    curr_point = np.array([v for k, v in init_val_dict.items()])
    history = [curr_point.tolist()]
    
    for i in range(max_iter):
        
        prev_point =curr_point
        prev_val_dict = {var: val for var, val in zip(variables, prev_point)}
        f_grad =f.gradient_at(prev_val_dict)

        curr_point =curr_point - learning_rate*f_grad
        history.append(curr_point.tolist())
        if np.linalg.norm(curr_point-prev_point, ord=2) < stop_stepsize: break
        
    if return_history:
        return history

    return {var: val for var, val in zip(variables, curr_point)}

def newton(

f, init_val_dict, max_iter=1000, stop_stepsize=1e-08, return_history=False)

Newton's Method finding minimum for a single expression

INPUTS

f: expression init_val_dict:dictionary containing initial value of variables max_itr: maximum iteration before the algorithm stops stop_stepsize: tolerance, the minimum threshold for absolute difference of value of f from 0 for the algorithm to stop return_history: default set to False. If True, return the trajectory of the algorithm including the final answer

RETURNS

If return_history = False: variable values corresponding to the minimum value of f If return_history = True, return the trajectory of the algorithm including the final answer

def newton(f,  init_val_dict, max_iter=1000, stop_stepsize=1e-8,return_history=False):
    """
    Newton's Method finding minimum for a single expression
    
    INPUTS
    =======
    f: expression 
    init_val_dict:dictionary containing initial value of variables
    max_itr: maximum iteration before the algorithm stops
    stop_stepsize: tolerance, the minimum threshold for absolute 
    difference of value of f from 0 for the algorithm to stop
    return_history: default set to False. If True, return the trajectory
    of the algorithm including the final answer
    
    RETURNS
    ========
    If return_history = False: variable values corresponding to the 
    minimum value of f
    If return_history = True, return the trajectory
    of the algorithm including the final answer
    """
    variables  = [var for var in init_val_dict.keys()]
    curr_point = np.array([v for k, v in init_val_dict.items()])
    f_grad = f.gradient_at(init_val_dict)
    f_hess = f.hessian_at(init_val_dict)
    history = [curr_point.tolist()]
    
    for i in range(max_iter):
        
        curr_val_dict = {var: val for var, val in zip(variables, curr_point)}
        # solve (Hessian of f at x)s = - (gradient of f at x)
        f_grad =f.gradient_at(curr_val_dict)
        f_hess = f.hessian_at(curr_val_dict)

        step = np.linalg.solve(f_hess, -f_grad)
        if np.linalg.norm(step, ord=2) < stop_stepsize: break
        
        # x := x + s
        curr_point = curr_point + step
        history.append(curr_point.tolist())
    
    if return_history:
        return history

    return {var: val for var, val in zip(variables, curr_point)}

Autodiff.plot

plot API documentation Top

plot module

import autodiff.optimize as opt
import matplotlib.pyplot as plt
import numpy as np

def plot_contour(f, init_val_dict, x,y,plot_range=[-3,3],method = 'gradient_descent'):
    """This function plots a countour map according to the values of 
    expression of interests. It finds the minimum point using either Gradient 
    Descent or Newton's Method and then color it on the contour map.
    
    INPUTS
    =======
    f: expression containing two sub_expressions
    init_val_dict: a dictionary containing variable name and values.
    x: expression 1, represented by x axis
    y: expression 2, represented by y axis
    plot_range: the range of both axes
    method: method with which the function finds the minimum point
    """
    if method == 'gradient_descent':
        a=opt.gradient_descent(f, init_val_dict,return_history=True)
    elif method =='newton':
        a=opt.newton(f, init_val_dict,return_history=True)
    #first plot the contour
    xx=np.linspace(plot_range[0],plot_range[1],100)
    yy=np.linspace(plot_range[0],plot_range[1],100)
    xg,yg = np.meshgrid(xx,yy)
    z=np.zeros(shape=(len(xg.ravel()),))
    for i,val in enumerate(xg.ravel()):
        vals = yg.ravel()
        z[i]=f.evaluation_at({x:val,y:vals[i]})
    z2 = z.reshape(xg.shape)
    plt.contourf(xg, yg, z2, alpha = 0.8, cmap = "BuGn")
    #plot the steps
    f_gd = []
    x_gd = []
    y_gd = []
    for l in a:
        x_gd.append(l[0])
        y_gd.append(l[0])
        #f_gd.append(f.evaluation_at({x:l[0],y:l[1]}))
    plt.plot(x_gd,y_gd,'.',alpha=0.1)
    plt.show()

Functions

def plot_contour(

f, init_val_dict, x, y, plot_range=[-3, 3], method='gradient_descent')

This function plots a countour map according to the values of expression of interests. It finds the minimum point using either Gradient Descent or Newton's Method and then color it on the contour map.

INPUTS

f: expression containing two sub_expressions init_val_dict: a dictionary containing variable name and values. x: expression 1, represented by x axis y: expression 2, represented by y axis plot_range: the range of both axes method: method with which the function finds the minimum point

def plot_contour(f, init_val_dict, x,y,plot_range=[-3,3],method = 'gradient_descent'):
    """This function plots a countour map according to the values of 
    expression of interests. It finds the minimum point using either Gradient 
    Descent or Newton's Method and then color it on the contour map.
    
    INPUTS
    =======
    f: expression containing two sub_expressions
    init_val_dict: a dictionary containing variable name and values.
    x: expression 1, represented by x axis
    y: expression 2, represented by y axis
    plot_range: the range of both axes
    method: method with which the function finds the minimum point
    """
    if method == 'gradient_descent':
        a=opt.gradient_descent(f, init_val_dict,return_history=True)
    elif method =='newton':
        a=opt.newton(f, init_val_dict,return_history=True)
    #first plot the contour
    xx=np.linspace(plot_range[0],plot_range[1],100)
    yy=np.linspace(plot_range[0],plot_range[1],100)
    xg,yg = np.meshgrid(xx,yy)
    z=np.zeros(shape=(len(xg.ravel()),))
    for i,val in enumerate(xg.ravel()):
        vals = yg.ravel()
        z[i]=f.evaluation_at({x:val,y:vals[i]})
    z2 = z.reshape(xg.shape)
    plt.contourf(xg, yg, z2, alpha = 0.8, cmap = "BuGn")
    #plot the steps
    f_gd = []
    x_gd = []
    y_gd = []
    for l in a:
        x_gd.append(l[0])
        y_gd.append(l[0])
        #f_gd.append(f.evaluation_at({x:l[0],y:l[1]}))
    plt.plot(x_gd,y_gd,'.',alpha=0.1)
    plt.show()